diff options
-rw-r--r-- | gold/output.cc | 134 | ||||
-rw-r--r-- | gold/output.h | 21 |
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(); |