aboutsummaryrefslogtreecommitdiff
path: root/gold
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@google.com>2007-12-04 23:42:28 +0000
committerIan Lance Taylor <iant@google.com>2007-12-04 23:42:28 +0000
commitc420411fe81ad583b2154fd9338fd0076761d99d (patch)
treeeb2c820750d18834ce12a96361b56bf1c2f43882 /gold
parent6ccb916229b6180c23485cc27d06acd1715efbdd (diff)
downloadgdb-c420411fe81ad583b2154fd9338fd0076761d99d.zip
gdb-c420411fe81ad583b2154fd9338fd0076761d99d.tar.gz
gdb-c420411fe81ad583b2154fd9338fd0076761d99d.tar.bz2
From Craig Silverstein: Support irregular output files.
Diffstat (limited to 'gold')
-rw-r--r--gold/output.cc95
-rw-r--r--gold/output.h12
2 files changed, 87 insertions, 20 deletions
diff --git a/gold/output.cc b/gold/output.cc
index 59658c7..420282a 100644
--- a/gold/output.cc
+++ b/gold/output.cc
@@ -38,6 +38,11 @@
#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));
diff --git a/gold/output.h b/gold/output.h
index fafd62b..fe42112 100644
--- a/gold/output.h
+++ b/gold/output.h
@@ -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.