PR ld/10515
[binutils-gdb.git] / gold / output.cc
index 145fca159df5f00e0fb4bc7c3e6af3c592b9d5ac..64fcb37187b5cbc2a004e803d6a721f00e8afe8d 100644 (file)
@@ -1,6 +1,6 @@
 // output.cc -- manage the output file for gold
 
-// Copyright 2006, 2007, 2008 Free Software Foundation, Inc.
+// Copyright 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
 # define MAP_ANONYMOUS  MAP_ANON
 #endif
 
+#ifndef HAVE_POSIX_FALLOCATE
+// A dummy, non general, version of posix_fallocate.  Here we just set
+// the file size and hope that there is enough disk space.  FIXME: We
+// could allocate disk space by walking block by block and writing a
+// zero byte into each block.
+static int
+posix_fallocate(int o, off_t offset, off_t len)
+{
+  return ftruncate(o, offset + len);
+}
+#endif // !defined(HAVE_POSIX_FALLOCATE)
+
 namespace gold
 {
 
@@ -420,7 +432,6 @@ Output_file_header::do_sized_write(Output_file* of)
                              ? elfcpp::ELFDATA2MSB
                              : elfcpp::ELFDATA2LSB);
   e_ident[elfcpp::EI_VERSION] = elfcpp::EV_CURRENT;
-  // FIXME: Some targets may need to set EI_OSABI and EI_ABIVERSION.
   oehdr.put_e_ident(e_ident);
 
   elfcpp::ET e_type;
@@ -477,6 +488,10 @@ Output_file_header::do_sized_write(Output_file* of)
   else
     oehdr.put_e_shstrndx(elfcpp::SHN_XINDEX);
 
+  // Let the target adjust the ELF header, e.g., to set EI_OSABI in
+  // the e_ident field.
+  parameters->target().adjust_elf_header(view, ehdr_size);
+
   of->write_output_view(0, ehdr_size, view);
 }
 
@@ -838,11 +853,11 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::
   Output_section* os = this->u1_.relobj->output_section(lsi);
   gold_assert(os != NULL);
   Address offset = this->u1_.relobj->get_output_section_offset(lsi);
-  if (offset != -1U)
+  if (offset != invalid_address)
     return offset + addend;
   // This is a merge section.
   offset = os->output_address(this->u1_.relobj, lsi, addend);
-  gold_assert(offset != -1U);
+  gold_assert(offset != invalid_address);
   return offset;
 }
 
@@ -858,13 +873,13 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::get_address() const
       Output_section* os = this->u2_.relobj->output_section(this->shndx_);
       gold_assert(os != NULL);
       Address off = this->u2_.relobj->get_output_section_offset(this->shndx_);
-      if (off != -1U)
+      if (off != invalid_address)
        address += os->address() + off;
       else
        {
          address = os->output_address(this->u2_.relobj, this->shndx_,
                                       address);
-         gold_assert(address != -1U);
+         gold_assert(address != invalid_address);
        }
     }
   else if (this->u2_.od != NULL)
@@ -1749,6 +1764,8 @@ Output_section::Output_section(const char* name, elfcpp::Elf_Word type,
     attached_input_sections_are_sorted_(false),
     is_relro_(false),
     is_relro_local_(false),
+    is_small_section_(false),
+    is_large_section_(false),
     tls_offset_(0)
 {
   // An unallocated section has no address.  Forcing this means that
@@ -1978,6 +1995,24 @@ Output_section::add_merge_input_section(Relobj* object, unsigned int shndx,
   return true;
 }
 
+// Update the output section flags based on input section flags.
+
+void
+Output_section::update_flags_for_input_section(elfcpp::Elf_Xword flags)
+{
+  // If we created the section with SHF_ALLOC clear, we set the
+  // address.  If we are now setting the SHF_ALLOC flag, we need to
+  // undo that.
+  if ((this->flags_ & elfcpp::SHF_ALLOC) == 0
+      && (flags & elfcpp::SHF_ALLOC) != 0)
+    this->mark_address_invalid();
+
+  this->flags_ |= (flags
+                  & (elfcpp::SHF_WRITE
+                     | elfcpp::SHF_ALLOC
+                     | elfcpp::SHF_EXECINSTR));
+}
+
 // Given an address OFFSET relative to the start of input section
 // SHNDX in OBJECT, return whether this address is being included in
 // the final link.  This should only be called if SHNDX in OBJECT has
@@ -2043,7 +2078,7 @@ Output_section::output_address(const Relobj* object, unsigned int shndx,
       if (p->output_offset(object, shndx, offset, &output_offset))
        {
          if (output_offset == -1)
-           return -1U;
+           return -1ULL;
          return addr + output_offset;
        }
       addr += p->data_size();
@@ -2057,12 +2092,13 @@ Output_section::output_address(const Relobj* object, unsigned int shndx,
   gold_unreachable();
 }
 
-// Return the output address of the start of the merged section for
+// Find the output address of the start of the merged section for
 // input section SHNDX in object OBJECT.
 
-uint64_t
-Output_section::starting_output_address(const Relobj* object,
-                                       unsigned int shndx) const
+bool
+Output_section::find_starting_output_address(const Relobj* object,
+                                            unsigned int shndx,
+                                            uint64_t* paddr) const
 {
   uint64_t addr = this->address() + this->first_input_offset_;
   for (Input_section_list::const_iterator p = this->input_sections_.begin();
@@ -2076,11 +2112,16 @@ Output_section::starting_output_address(const Relobj* object,
       // Unfortunately we don't know for sure that input offset 0 is
       // mapped at all.
       if (p->is_merge_section_for(object, shndx))
-       return addr;
+       {
+         *paddr = addr;
+         return true;
+       }
 
       addr += p->data_size();
     }
-  gold_unreachable();
+
+  // We couldn't find a merge output section for this input section.
+  return false;
 }
 
 // Set the data size of an Output_section.  This is where we handle
@@ -2592,7 +2633,8 @@ Output_segment::Output_segment(elfcpp::Elf_Word type, elfcpp::Elf_Word flags)
     type_(type),
     flags_(flags),
     is_max_align_known_(false),
-    are_addresses_set_(false)
+    are_addresses_set_(false),
+    is_large_data_segment_(false)
 {
 }
 
@@ -2604,6 +2646,7 @@ Output_segment::add_output_section(Output_section* os,
 {
   gold_assert((os->flags() & elfcpp::SHF_ALLOC) != 0);
   gold_assert(!this->is_max_align_known_);
+  gold_assert(os->is_large_data_section() == this->is_large_data_segment());
 
   // Update the segment flags.
   this->flags_ |= seg_flags;
@@ -2651,8 +2694,7 @@ Output_segment::add_output_section(Output_section* os,
   // and the PT_TLS segment -- we do this grouping only for the
   // PT_LOAD segment.
   if (this->type_ != elfcpp::PT_TLS
-      && (os->flags() & elfcpp::SHF_TLS) != 0
-      && !this->output_data_.empty())
+      && (os->flags() & elfcpp::SHF_TLS) != 0)
     {
       pdl = &this->output_data_;
       bool nobits = os->type() == elfcpp::SHT_NOBITS;
@@ -2712,6 +2754,69 @@ Output_segment::add_output_section(Output_section* os,
       return;
     }
 
+  // Small data sections go at the end of the list of data sections.
+  // If OS is not small, and there are small sections, we have to
+  // insert it before the first small section.
+  if (os->type() != elfcpp::SHT_NOBITS
+      && !os->is_small_section()
+      && !pdl->empty()
+      && pdl->back()->is_section()
+      && pdl->back()->output_section()->is_small_section())
+    {
+      for (Output_segment::Output_data_list::iterator p = pdl->begin();
+          p != pdl->end();
+          ++p)
+       {
+         if ((*p)->is_section()
+             && (*p)->output_section()->is_small_section())
+           {
+             pdl->insert(p, os);
+             return;
+           }
+       }
+      gold_unreachable();
+    }
+
+  // A small BSS section goes at the start of the BSS sections, after
+  // other small BSS sections.
+  if (os->type() == elfcpp::SHT_NOBITS && os->is_small_section())
+    {
+      for (Output_segment::Output_data_list::iterator p = pdl->begin();
+          p != pdl->end();
+          ++p)
+       {
+         if (!(*p)->is_section()
+             || !(*p)->output_section()->is_small_section())
+           {
+             pdl->insert(p, os);
+             return;
+           }
+       }
+    }
+
+  // A large BSS section goes at the end of the BSS sections, which
+  // means that one that is not large must come before the first large
+  // one.
+  if (os->type() == elfcpp::SHT_NOBITS
+      && !os->is_large_section()
+      && !pdl->empty()
+      && pdl->back()->is_section()
+      && pdl->back()->output_section()->is_large_section())
+    {
+      for (Output_segment::Output_data_list::iterator p = pdl->begin();
+          p != pdl->end();
+          ++p)
+       {
+         if ((*p)->is_section()
+             && (*p)->output_section()->is_large_section())
+           {
+             pdl->insert(p, os);
+             return;
+           }
+       }
+      gold_unreachable();
+    }
+
   pdl->push_back(os);
 }
 
@@ -2777,7 +2882,9 @@ Output_segment::maximum_alignment()
       // segment is a relro section, then the segment must be aligned
       // to at least the common page size.  This ensures that the
       // PT_GNU_RELRO segment will start at a page boundary.
-      if (parameters->options().relro() && this->is_first_section_relro())
+      if (this->type_ == elfcpp::PT_LOAD
+         && parameters->options().relro()
+         && this->is_first_section_relro())
        {
          addralign = parameters->target().common_pagesize();
          if (addralign > this->max_align_)
@@ -3358,6 +3465,17 @@ Output_file::resize(off_t file_size)
     }
 }
 
+// Map a block of memory which will later be written to the file.
+// Return a pointer to the memory.
+
+void*
+Output_file::map_anonymous()
+{
+  this->map_is_anonymous_ = true;
+  return ::mmap(NULL, this->file_size_, PROT_READ | PROT_WRITE,
+               MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+}
+
 // Map the file into memory.
 
 void
@@ -3374,27 +3492,37 @@ Output_file::map()
       || ::fstat(o, &statbuf) != 0
       || !S_ISREG(statbuf.st_mode)
       || this->is_temporary_)
-    {
-      this->map_is_anonymous_ = true;
-      base = ::mmap(NULL, this->file_size_, PROT_READ | PROT_WRITE,
-                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-    }
+    base = this->map_anonymous();
   else
     {
-      // Write out one byte to make the file the right size.
-      if (::lseek(o, this->file_size_ - 1, SEEK_SET) < 0)
-        gold_fatal(_("%s: lseek: %s"), this->name_, strerror(errno));
-      char b = 0;
-      if (::write(o, &b, 1) != 1)
-        gold_fatal(_("%s: write: %s"), this->name_, strerror(errno));
+      // Ensure that we have disk space available for the file.  If we
+      // don't do this, it is possible that we will call munmap,
+      // close, and exit with dirty buffers still in the cache with no
+      // assigned disk blocks.  If the disk is out of space at that
+      // point, the output file will wind up incomplete, but we will
+      // have already exited.  The alternative to fallocate would be
+      // to use fdatasync, but that would be a more significant
+      // performance hit.
+      if (::posix_fallocate(o, 0, this->file_size_) < 0)
+       gold_fatal(_("%s: %s"), this->name_, strerror(errno));
 
       // Map the file into memory.
       this->map_is_anonymous_ = false;
       base = ::mmap(NULL, this->file_size_, PROT_READ | PROT_WRITE,
                     MAP_SHARED, o, 0);
+
+      // The mmap call might fail because of file system issues: the
+      // file system might not support mmap at all, or it might not
+      // support mmap with PROT_WRITE.  I'm not sure which errno
+      // values we will see in all cases, so if the mmap fails for any
+      // reason try for an anonymous map.
+      if (base == MAP_FAILED)
+       base = this->map_anonymous();
     }
   if (base == MAP_FAILED)
-    gold_fatal(_("%s: mmap: %s"), this->name_, strerror(errno));
+    gold_fatal(_("%s: mmap: failed to allocate %lu bytes for output file: %s"),
+              this->name_, static_cast<unsigned long>(this->file_size_),
+              strerror(errno));
   this->base_ = static_cast<unsigned char*>(base);
 }
 
@@ -3417,15 +3545,20 @@ Output_file::close()
   if (this->map_is_anonymous_ && !this->is_temporary_)
     {
       size_t bytes_to_write = this->file_size_;
+      size_t offset = 0;
       while (bytes_to_write > 0)
         {
-          ssize_t bytes_written = ::write(this->o_, this->base_, bytes_to_write);
+          ssize_t bytes_written = ::write(this->o_, this->base_ + offset,
+                                          bytes_to_write);
           if (bytes_written == 0)
             gold_error(_("%s: write: unexpected 0 return-value"), this->name_);
           else if (bytes_written < 0)
             gold_error(_("%s: write: %s"), this->name_, strerror(errno));
           else
-            bytes_to_write -= bytes_written;
+            {
+              bytes_to_write -= bytes_written;
+              offset += bytes_written;
+            }
         }
     }
   this->unmap();
@@ -3490,6 +3623,86 @@ Output_section::add_input_section<64, true>(
     bool have_sections_script);
 #endif
 
+#ifdef HAVE_TARGET_32_LITTLE
+template
+class Output_reloc<elfcpp::SHT_REL, false, 32, false>;
+#endif
+
+#ifdef HAVE_TARGET_32_BIG
+template
+class Output_reloc<elfcpp::SHT_REL, false, 32, true>;
+#endif
+
+#ifdef HAVE_TARGET_64_LITTLE
+template
+class Output_reloc<elfcpp::SHT_REL, false, 64, false>;
+#endif
+
+#ifdef HAVE_TARGET_64_BIG
+template
+class Output_reloc<elfcpp::SHT_REL, false, 64, true>;
+#endif
+
+#ifdef HAVE_TARGET_32_LITTLE
+template
+class Output_reloc<elfcpp::SHT_REL, true, 32, false>;
+#endif
+
+#ifdef HAVE_TARGET_32_BIG
+template
+class Output_reloc<elfcpp::SHT_REL, true, 32, true>;
+#endif
+
+#ifdef HAVE_TARGET_64_LITTLE
+template
+class Output_reloc<elfcpp::SHT_REL, true, 64, false>;
+#endif
+
+#ifdef HAVE_TARGET_64_BIG
+template
+class Output_reloc<elfcpp::SHT_REL, true, 64, true>;
+#endif
+
+#ifdef HAVE_TARGET_32_LITTLE
+template
+class Output_reloc<elfcpp::SHT_RELA, false, 32, false>;
+#endif
+
+#ifdef HAVE_TARGET_32_BIG
+template
+class Output_reloc<elfcpp::SHT_RELA, false, 32, true>;
+#endif
+
+#ifdef HAVE_TARGET_64_LITTLE
+template
+class Output_reloc<elfcpp::SHT_RELA, false, 64, false>;
+#endif
+
+#ifdef HAVE_TARGET_64_BIG
+template
+class Output_reloc<elfcpp::SHT_RELA, false, 64, true>;
+#endif
+
+#ifdef HAVE_TARGET_32_LITTLE
+template
+class Output_reloc<elfcpp::SHT_RELA, true, 32, false>;
+#endif
+
+#ifdef HAVE_TARGET_32_BIG
+template
+class Output_reloc<elfcpp::SHT_RELA, true, 32, true>;
+#endif
+
+#ifdef HAVE_TARGET_64_LITTLE
+template
+class Output_reloc<elfcpp::SHT_RELA, true, 64, false>;
+#endif
+
+#ifdef HAVE_TARGET_64_BIG
+template
+class Output_reloc<elfcpp::SHT_RELA, true, 64, true>;
+#endif
+
 #ifdef HAVE_TARGET_32_LITTLE
 template
 class Output_data_reloc<elfcpp::SHT_REL, false, 32, false>;