aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gold/output.cc134
-rw-r--r--gold/output.h21
2 files changed, 112 insertions, 43 deletions
diff --git a/gold/output.cc b/gold/output.cc
index 64fcb37..6a37b43 100644
--- a/gold/output.cc
+++ b/gold/output.cc
@@ -3397,6 +3397,42 @@ Output_file::Output_file(const char* name)
{
}
+// Try to open an existing file. Returns false if the file doesn't
+// exist, has a size of 0 or can't be mmapped.
+
+bool
+Output_file::open_for_modification()
+{
+ // The name "-" means "stdout".
+ if (strcmp(this->name_, "-") == 0)
+ return false;
+
+ // Don't bother opening files with a size of zero.
+ struct stat s;
+ if (::stat(this->name_, &s) != 0 || s.st_size == 0)
+ return false;
+
+ int o = open_descriptor(-1, this->name_, O_RDWR, 0);
+ if (o < 0)
+ gold_fatal(_("%s: open: %s"), this->name_, strerror(errno));
+ this->o_ = o;
+ this->file_size_ = s.st_size;
+
+ // If the file can't be mmapped, copying the content to an anonymous
+ // map will probably negate the performance benefits of incremental
+ // linking. This could be helped by using views and loading only
+ // the necessary parts, but this is not supported as of now.
+ if (!this->map_no_anonymous())
+ {
+ release_descriptor(o, true);
+ this->o_ = -1;
+ this->file_size_ = 0;
+ return false;
+ }
+
+ return true;
+}
+
// Open the output file.
void
@@ -3465,21 +3501,27 @@ 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.
+// Map an anonymous block of memory which will later be written to the
+// file. Return whether the map succeeded.
-void*
+bool
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);
+ void* base = ::mmap(NULL, this->file_size_, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (base != MAP_FAILED)
+ {
+ this->map_is_anonymous_ = true;
+ this->base_ = static_cast<unsigned char*>(base);
+ return true;
+ }
+ return false;
}
-// Map the file into memory.
+// Map the file into memory. Return whether the mapping succeeded.
-void
-Output_file::map()
+bool
+Output_file::map_no_anonymous()
{
const int o = this->o_;
@@ -3492,38 +3534,52 @@ Output_file::map()
|| ::fstat(o, &statbuf) != 0
|| !S_ISREG(statbuf.st_mode)
|| this->is_temporary_)
- base = this->map_anonymous();
- else
- {
- // 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();
- }
+ return false;
+
+ // 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.
+ 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.
if (base == MAP_FAILED)
- gold_fatal(_("%s: mmap: failed to allocate %lu bytes for output file: %s"),
- this->name_, static_cast<unsigned long>(this->file_size_),
- strerror(errno));
+ return false;
+
+ this->map_is_anonymous_ = false;
this->base_ = static_cast<unsigned char*>(base);
+ return true;
+}
+
+// Map the file into memory.
+
+void
+Output_file::map()
+{
+ if (this->map_no_anonymous())
+ return;
+
+ // 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 and we
+ // don't care about file contents, try for an anonymous map.
+ if (this->map_anonymous())
+ return;
+
+ gold_fatal(_("%s: mmap: failed to allocate %lu bytes for output file: %s"),
+ this->name_, static_cast<unsigned long>(this->file_size_),
+ strerror(errno));
}
// Unmap the file from memory.
diff --git a/gold/output.h b/gold/output.h
index f9cbfa6..7bd0cf3 100644
--- a/gold/output.h
+++ b/gold/output.h
@@ -3093,16 +3093,24 @@ class Output_file
set_is_temporary()
{ this->is_temporary_ = true; }
+ // Try to open an existing file. Returns false if the file doesn't
+ // exist, has a size of 0 or can't be mmaped. This method is
+ // thread-unsafe.
+ bool
+ open_for_modification();
+
// Open the output file. FILE_SIZE is the final size of the file.
+ // If the file already exists, it is deleted/truncated. This method
+ // is thread-unsafe.
void
open(off_t file_size);
- // Resize the output file.
+ // Resize the output file. This method is thread-unsafe.
void
resize(off_t file_size);
// Close the output file (flushing all buffered data) and make sure
- // there are no errors.
+ // there are no errors. This method is thread-unsafe.
void
close();
@@ -3153,14 +3161,19 @@ class Output_file
{ }
private:
- // Map the file into memory.
+ // Map the file into memory or, if that fails, allocate anonymous
+ // memory.
void
map();
// Allocate anonymous memory for the file.
- void*
+ bool
map_anonymous();
+ // Map the file into memory.
+ bool
+ map_no_anonymous();
+
// Unmap the file from memory (and flush to disk buffers).
void
unmap();