From Craig Silverstein: Support irregular output files.
authorIan Lance Taylor <iant@google.com>
Tue, 4 Dec 2007 23:42:28 +0000 (23:42 +0000)
committerIan Lance Taylor <iant@google.com>
Tue, 4 Dec 2007 23:42:28 +0000 (23:42 +0000)
gold/output.cc
gold/output.h

index 59658c79942e3aba0f8108221d03174712b59836..420282addb2ee6ddf6eca58b6e9c5496a8e637c2 100644 (file)
 #include "merge.h"
 #include "output.h"
 
+// Some BSD systems still use MAP_ANON instead of MAP_ANONYMOUS
+#ifndef MAP_ANONYMOUS
+# define MAP_ANONYMOUS  MAP_ANON
+#endif
+
 namespace gold
 {
 
@@ -1927,7 +1932,8 @@ Output_file::Output_file(const General_options& options, Target* target)
     name_(options.output_file_name()),
     o_(-1),
     file_size_(0),
-    base_(NULL)
+    base_(NULL),
+    map_is_anonymous_(false)
 {
 }
 
@@ -1969,10 +1975,24 @@ Output_file::open(off_t file_size)
 void
 Output_file::resize(off_t file_size)
 {
-  if (::munmap(this->base_, this->file_size_) < 0)
-    gold_error(_("%s: munmap: %s"), this->name_, strerror(errno));
-  this->file_size_ = file_size;
-  this->map();
+  // If the mmap is mapping an anonymous memory buffer, this is easy:
+  // just mremap to the new size.  If it's mapping to a file, we want
+  // to unmap to flush to the file, then remap after growing the file.
+  if (this->map_is_anonymous_)
+    {
+      void* base = ::mremap(this->base_, this->file_size_, file_size,
+                            MREMAP_MAYMOVE);
+      if (base == MAP_FAILED)
+        gold_fatal(_("%s: mremap: %s"), this->name_, strerror(errno));
+      this->base_ = static_cast<unsigned char*>(base);
+      this->file_size_ = file_size;
+    }
+  else
+    {
+      this->unmap();
+      this->file_size_ = file_size;
+      this->map();
+    }
 }
 
 // Map the file into memory.
@@ -1980,31 +2000,70 @@ Output_file::resize(off_t file_size)
 void
 Output_file::map()
 {
-  int o = this->o_;
-
-  // 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));
+  const int o = this->o_;
 
-  // Map the file into memory.
-  void* base = ::mmap(NULL, this->file_size_, PROT_READ | PROT_WRITE,
-                     MAP_SHARED, o, 0);
+  // If the output file is not a regular file, don't try to mmap it;
+  // instead, we'll mmap a block of memory (an anonymous buffer), and
+  // then later write the buffer to the file.
+  void* base;
+  struct stat statbuf;
+  if (::fstat(o, &statbuf) != 0
+      || !S_ISREG(statbuf.st_mode))
+    {
+      this->map_is_anonymous_ = true;
+      base = ::mmap(NULL, this->file_size_, PROT_READ | PROT_WRITE,
+                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+    }
+  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));
+
+      // Map the file into memory.
+      this->map_is_anonymous_ = false;
+      base = ::mmap(NULL, this->file_size_, PROT_READ | PROT_WRITE,
+                    MAP_SHARED, o, 0);
+    }
   if (base == MAP_FAILED)
     gold_fatal(_("%s: mmap: %s"), this->name_, strerror(errno));
   this->base_ = static_cast<unsigned char*>(base);
 }
 
-// Close the output file.
+// Unmap the file from memory.
 
 void
-Output_file::close()
+Output_file::unmap()
 {
   if (::munmap(this->base_, this->file_size_) < 0)
     gold_error(_("%s: munmap: %s"), this->name_, strerror(errno));
   this->base_ = NULL;
+}
+
+// Close the output file.
+
+void
+Output_file::close()
+{
+  // If the map isn't file-backed, we need to write it now.
+  if (this->map_is_anonymous_)
+    {
+      size_t bytes_to_write = this->file_size_;
+      while (bytes_to_write > 0)
+        {
+          ssize_t bytes_written = ::write(this->o_, this->base_, 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;
+        }
+    }
+  this->unmap();
 
   if (::close(this->o_) < 0)
     gold_error(_("%s: close: %s"), this->name_, strerror(errno));
index fafd62b1faa0ecc11b38f6dd8538b1d28fee7ed9..fe421129470fbe8d0eb85eaaed05bd709b78d3ab 100644 (file)
@@ -2117,7 +2117,8 @@ class Output_file
   void
   resize(off_t file_size);
 
-  // Close the output file and make sure there are no error.
+  // Close the output file (flushing all buffered data) and make sure
+  // there are no errors.
   void
   close();
 
@@ -2167,10 +2168,15 @@ class Output_file
   { }
 
  private:
-  // Map the file into memory.
+  // Map the file into memory and return a pointer to the map.
   void
   map();
 
+  // Unmap the file from memory (and flush to disk buffers).
+  void
+  unmap();
+
+
   // General options.
   const General_options& options_;
   // Target.
@@ -2183,6 +2189,8 @@ class Output_file
   off_t file_size_;
   // Base of file mapped into memory.
   unsigned char* base_;
+  // True iff base_ points to a memory buffer rather than an output file.
+  bool map_is_anonymous_;
 };
 
 } // End namespace gold.
This page took 0.031197 seconds and 4 git commands to generate.