From Craig Silverstein: Support irregular output files.

This commit is contained in:
Ian Lance Taylor 2007-12-04 23:42:28 +00:00
parent 6ccb916229
commit c420411fe8
2 changed files with 87 additions and 20 deletions

View file

@ -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_;
const 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));
// 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.
void* base = ::mmap(NULL, this->file_size_, PROT_READ | PROT_WRITE,
MAP_SHARED, o, 0);
// 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);
}
// Unmap the file from memory.
void
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 (::munmap(this->base_, this->file_size_) < 0)
gold_error(_("%s: munmap: %s"), this->name_, strerror(errno));
this->base_ = NULL;
// 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));

View 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.