From c420411fe81ad583b2154fd9338fd0076761d99d Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 4 Dec 2007 23:42:28 +0000 Subject: [PATCH] From Craig Silverstein: Support irregular output files. --- gold/output.cc | 95 ++++++++++++++++++++++++++++++++++++++++---------- gold/output.h | 12 +++++-- 2 files changed, 87 insertions(+), 20 deletions(-) diff --git a/gold/output.cc b/gold/output.cc index 59658c79942..420282addb2 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(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(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 fafd62b1faa..fe421129470 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. -- 2.30.2