diff options
Diffstat (limited to 'gold/fileread.cc')
-rw-r--r-- | gold/fileread.cc | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/gold/fileread.cc b/gold/fileread.cc new file mode 100644 index 000000000000..1a142022569a --- /dev/null +++ b/gold/fileread.cc @@ -0,0 +1,379 @@ +// fileread.cc -- read files for gold + +#include "gold.h" + +#include <cstring> +#include <cerrno> +#include <fcntl.h> +#include <unistd.h> + +#include "options.h" +#include "dirsearch.h" +#include "fileread.h" + +namespace gold +{ + +// Class File_read::View. + +File_read::View::~View() +{ + gold_assert(!this->is_locked()); + delete[] this->data_; +} + +void +File_read::View::lock() +{ + ++this->lock_count_; +} + +void +File_read::View::unlock() +{ + gold_assert(this->lock_count_ > 0); + --this->lock_count_; +} + +bool +File_read::View::is_locked() +{ + return this->lock_count_ > 0; +} + +// Class File_read. + +// The File_read class is designed to support file descriptor caching, +// but this is not currently implemented. + +File_read::~File_read() +{ + gold_assert(this->lock_count_ == 0); + if (this->descriptor_ >= 0) + { + if (close(this->descriptor_) < 0) + fprintf(stderr, _("%s: warning: close(%s) failed: %s"), + program_name, this->name_.c_str(), strerror(errno)); + this->descriptor_ = -1; + } + this->name_.clear(); + this->clear_views(true); +} + +// Open the file. + +bool +File_read::open(const std::string& name) +{ + gold_assert(this->lock_count_ == 0 + && this->descriptor_ < 0 + && this->name_.empty()); + this->name_ = name; + this->descriptor_ = ::open(this->name_.c_str(), O_RDONLY); + ++this->lock_count_; + return this->descriptor_ >= 0; +} + +// Open the file for testing purposes. + +bool +File_read::open(const std::string& name, const unsigned char* contents, + off_t contents_size) +{ + gold_assert(this->lock_count_ == 0 + && this->descriptor_ < 0 + && this->name_.empty()); + this->name_ = name; + this->contents_ = contents; + this->contents_size_ = contents_size; + ++this->lock_count_; + return true; +} + +void +File_read::lock() +{ + ++this->lock_count_; +} + +void +File_read::unlock() +{ + gold_assert(this->lock_count_ > 0); + --this->lock_count_; +} + +bool +File_read::is_locked() +{ + return this->lock_count_ > 0; +} + +// See if we have a view which covers the file starting at START for +// SIZE bytes. Return a pointer to the View if found, NULL if not. + +inline File_read::View* +File_read::find_view(off_t start, off_t size) +{ + off_t page = File_read::page_offset(start); + Views::iterator p = this->views_.find(page); + if (p == this->views_.end()) + return NULL; + if (p->second->size() - (start - page) < size) + return NULL; + return p->second; +} + +// Read data from the file. Return the number of bytes read. If +// PBYTES is not NULL, store the number of bytes in *PBYTES, otherwise +// require that we read exactly the number of bytes requested. + +off_t +File_read::do_read(off_t start, off_t size, void* p, off_t* pbytes) +{ + gold_assert(this->lock_count_ > 0); + + off_t bytes; + if (this->contents_ == NULL) + { + int o = this->descriptor_; + + if (lseek(o, start, SEEK_SET) < 0) + { + fprintf(stderr, _("%s: %s: lseek to %lld failed: %s"), + program_name, this->filename().c_str(), + static_cast<long long>(start), + strerror(errno)); + gold_exit(false); + } + + bytes = ::read(o, p, size); + if (bytes < 0) + { + fprintf(stderr, _("%s: %s: read failed: %s\n"), + program_name, this->filename().c_str(), strerror(errno)); + gold_exit(false); + } + } + else + { + bytes = this->contents_size_ - start; + if (bytes < 0) + bytes = 0; + else if (bytes > size) + bytes = size; + memcpy(p, this->contents_ + start, bytes); + } + + if (pbytes != NULL) + *pbytes = bytes; + else if (bytes != size) + { + fprintf(stderr, + _("%s: %s: file too short: read only %lld of %lld " + "bytes at %lld\n"), + program_name, this->filename().c_str(), + static_cast<long long>(bytes), + static_cast<long long>(size), + static_cast<long long>(start)); + gold_exit(false); + } + + return bytes; +} + +void +File_read::read(off_t start, off_t size, void* p, off_t* pbytes) +{ + gold_assert(this->lock_count_ > 0); + + File_read::View* pv = this->find_view(start, size); + if (pv != NULL) + { + memcpy(p, pv->data() + (start - pv->start()), size); + if (pbytes != NULL) + *pbytes = size; + return; + } + + this->do_read(start, size, p, pbytes); +} + +// Find an existing view or make a new one. + +File_read::View* +File_read::find_or_make_view(off_t start, off_t size, off_t* pbytes) +{ + gold_assert(this->lock_count_ > 0); + + off_t poff = File_read::page_offset(start); + + File_read::View* const vnull = NULL; + std::pair<Views::iterator, bool> ins = + this->views_.insert(std::make_pair(poff, vnull)); + + if (!ins.second) + { + // There was an existing view at this offset. + File_read::View* v = ins.first->second; + if (v->size() - (start - v->start()) >= size) + { + if (pbytes != NULL) + *pbytes = size; + return v; + } + + // This view is not large enough. + this->saved_views_.push_back(v); + } + + // We need to read data from the file. + + off_t psize = File_read::pages(size + (start - poff)); + unsigned char* p = new unsigned char[psize]; + + off_t got_bytes; + off_t bytes = this->do_read(poff, psize, p, &got_bytes); + + File_read::View* v = new File_read::View(poff, bytes, p); + + ins.first->second = v; + + if (bytes - (start - poff) >= size) + { + if (pbytes != NULL) + *pbytes = size; + return v; + } + + if (pbytes != NULL) + { + *pbytes = bytes - (start - poff); + return v; + } + + fprintf(stderr, + _("%s: %s: file too short: read only %lld of %lld bytes at %lld\n"), + program_name, this->filename().c_str(), + static_cast<long long>(bytes - (start - poff)), + static_cast<long long>(size), + static_cast<long long>(start)); + gold_exit(false); +} + +// This implementation of get_view just reads into a memory buffer, +// which we store on view_list_. At some point we should support +// mmap. + +const unsigned char* +File_read::get_view(off_t start, off_t size, off_t* pbytes) +{ + gold_assert(this->lock_count_ > 0); + File_read::View* pv = this->find_or_make_view(start, size, pbytes); + return pv->data() + (start - pv->start()); +} + +File_view* +File_read::get_lasting_view(off_t start, off_t size, off_t* pbytes) +{ + gold_assert(this->lock_count_ > 0); + File_read::View* pv = this->find_or_make_view(start, size, pbytes); + pv->lock(); + return new File_view(*this, pv, pv->data() + (start - pv->start())); +} + +// Remove all the file views. + +void +File_read::clear_views(bool destroying) +{ + for (Views::iterator p = this->views_.begin(); + p != this->views_.end(); + ++p) + { + if (!p->second->is_locked()) + delete p->second; + else + { + gold_assert(!destroying); + this->saved_views_.push_back(p->second); + } + } + this->views_.clear(); + + Saved_views::iterator p = this->saved_views_.begin(); + while (p != this->saved_views_.end()) + { + if (!(*p)->is_locked()) + { + delete *p; + p = this->saved_views_.erase(p); + } + else + { + gold_assert(!destroying); + ++p; + } + } +} + +// Class File_view. + +File_view::~File_view() +{ + gold_assert(this->file_.is_locked()); + this->view_->unlock(); +} + +// Class Input_file. + +// Create a file for testing. + +Input_file::Input_file(const char* name, const unsigned char* contents, + off_t size) + : file_() +{ + this->input_argument_ = + new Input_file_argument(name, false, Position_dependent_options()); + bool ok = file_.open(name, contents, size); + gold_assert(ok); +} + +// Open the file. + +void +Input_file::open(const General_options& options, const Dirsearch& dirpath) +{ + std::string name; + if (!this->input_argument_->is_lib()) + name = this->input_argument_->name(); + else + { + std::string n1("lib"); + n1 += this->input_argument_->name(); + std::string n2; + if (options.is_static()) + n1 += ".a"; + else + { + n2 = n1 + ".a"; + n1 += ".so"; + } + name = dirpath.find(n1, n2); + if (name.empty()) + { + fprintf(stderr, _("%s: cannot find %s\n"), program_name, + this->input_argument_->name()); + gold_exit(false); + } + } + + if (!this->file_.open(name)) + { + fprintf(stderr, _("%s: cannot open %s: %s\n"), program_name, + name.c_str(), strerror(errno)); + gold_exit(false); + } +} + +} // End namespace gold. |