+2008-04-02 Ian Lance Taylor <iant@google.com>
+
+ * fileread.cc (File_read::find_view): Add byteshift and vshifted
+ parameters. Update for new key type to views_. Change all
+ callers.
+ (File_read::read): Adjust for byteshift in returned view.
+ (File_read::add_view): New function, broken out of
+ find_and_make_view.
+ (File_read::make_view): New function, broken out of
+ find_and_make_view.
+ (File_read::find_or_make_view): Add offset and aligned
+ parameters. Rewrite accordingly. Change all callers.
+ (File_read::get_view): Add offset and aligned parameters. Adjust
+ for byteshift in return value.
+ (File_read::get_lasting_view): Likewise.
+ * fileread.h (class File_read): Update declarations.
+ (class File_read::View): Add byteshift_ field. Add byteshift to
+ constructor. Add byteshift method.
+ * archive.h (Archive::clear_uncached_views): New function.
+ (Archive::get_view): Add aligned parameter. Change all callers.
+ * object.h (Object::get_view): Add aligned parameter. Change all
+ callers.
+ (Object::get_lasting_view): Likewise.
+
+ * fileread.cc (File_read::release): Don't call clear_views if
+ there are multiple objects.
+ * fileread.h (File_read::clear_uncached_views): New function.
+ * archive.cc (Add_archive_symbols::run): Call clear_uncached_views
+ on the archive.
+
2008-03-31 Cary Coutant <ccoutant@google.com>
Add thin archive support.
if (xname == "/")
{
const unsigned char* p = this->get_view(off + sizeof(Archive_header),
- extended_size, true);
+ extended_size, false, true);
const char* px = reinterpret_cast<const char*>(p);
this->extended_names_.assign(px, extended_size);
}
Archive::read_armap(off_t start, section_size_type size)
{
// Read in the entire armap.
- const unsigned char* p = this->get_view(start, size, false);
+ const unsigned char* p = this->get_view(start, size, true, false);
// Numbers in the armap are always big-endian.
const elfcpp::Elf_Word* pword = reinterpret_cast<const elfcpp::Elf_Word*>(p);
Archive::read_header(off_t off, bool cache, std::string* pname,
off_t* nested_off)
{
- const unsigned char* p = this->get_view(off, sizeof(Archive_header), cache);
+ const unsigned char* p = this->get_view(off, sizeof(Archive_header), true,
+ cache);
const Archive_header* hdr = reinterpret_cast<const Archive_header*>(p);
return this->interpret_header(hdr, off, pname, nested_off);
}
this->archive_->unlock_nested_archives();
this->archive_->release();
+ this->archive_->clear_uncached_views();
if (this->input_group_ != NULL)
this->input_group_->add_archive(this->archive_);
release()
{ this->input_file_->file().release(); }
+ // Clear uncached views in the underlying file.
+ void
+ clear_uncached_views()
+ { this->input_file_->file().clear_uncached_views(); }
+
// Unlock any nested archives.
void
unlock_nested_archives();
// Get a view into the underlying file.
const unsigned char*
- get_view(off_t start, section_size_type size, bool cache)
- { return this->input_file_->file().get_view(start, size, cache); }
+ get_view(off_t start, section_size_type size, bool aligned, bool cache)
+ { return this->input_file_->file().get_view(0, start, size, aligned, cache); }
// Read the archive symbol map.
void
}
section_size_type filesize = convert_to_section_size_type(f.filesize());
- const unsigned char* fileview = f.get_view(0, filesize, false);
+ const unsigned char* fileview = f.get_view(0, 0, filesize, false, false);
unsigned int align;
if (size == 32)
shndx, shdr.get_sh_link(), link);
*view = this->get_lasting_view(shdr.get_sh_offset(), shdr.get_sh_size(),
- false);
+ true, false);
*view_size = convert_to_section_size_type(shdr.get_sh_size());
*view_info = shdr.get_sh_info();
}
const off_t dynamic_size = dynamicshdr.get_sh_size();
const unsigned char* pdynamic = this->get_view(dynamicshdr.get_sh_offset(),
- dynamic_size, false);
+ dynamic_size, true, false);
const unsigned int link = dynamicshdr.get_sh_link();
if (link != strtab_shndx)
}
strtab_size = strtabshdr.get_sh_size();
- strtabu = this->get_view(strtabshdr.get_sh_offset(), strtab_size, false);
+ strtabu = this->get_view(strtabshdr.get_sh_offset(), strtab_size, false,
+ false);
}
const char* const strtab = reinterpret_cast<const char*>(strtabu);
gold_assert(dynsymshdr.get_sh_type() == elfcpp::SHT_DYNSYM);
sd->symbols = this->get_lasting_view(dynsymshdr.get_sh_offset(),
- dynsymshdr.get_sh_size(), false);
+ dynsymshdr.get_sh_size(), true,
+ false);
sd->symbols_size =
convert_to_section_size_type(dynsymshdr.get_sh_size());
sd->symbol_names = this->get_lasting_view(strtabshdr.get_sh_offset(),
strtabshdr.get_sh_size(),
- false);
+ false, false);
sd->symbol_names_size =
convert_to_section_size_type(strtabshdr.get_sh_size());
if (File_read::current_mapped_bytes > File_read::maximum_mapped_bytes)
File_read::maximum_mapped_bytes = File_read::current_mapped_bytes;
- this->clear_views(false);
+ // Only clear views if there is only one attached object. Otherwise
+ // we waste time trying to clear cached archive views.
+ if (this->object_count_ <= 1)
+ this->clear_views(false);
this->released_ = true;
}
// 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.
+// If BYTESHIFT is not -1U, the returned View must have the specified
+// byte shift; otherwise, it may have any byte shift. If VSHIFTED is
+// not NULL, this sets *VSHIFTED to a view which would have worked if
+// not for the requested BYTESHIFT.
inline File_read::View*
-File_read::find_view(off_t start, section_size_type size) const
+File_read::find_view(off_t start, section_size_type size,
+ unsigned int byteshift, File_read::View** vshifted) const
{
+ if (vshifted != NULL)
+ *vshifted = NULL;
+
off_t page = File_read::page_offset(start);
- Views::const_iterator p = this->views_.lower_bound(page);
- if (p == this->views_.end() || p->first > page)
+ unsigned int bszero = 0;
+ Views::const_iterator p = this->views_.upper_bound(std::make_pair(page - 1,
+ bszero));
+
+ while (p != this->views_.end() && p->first.first <= page)
{
- if (p == this->views_.begin())
- return NULL;
- --p;
- }
+ if (p->second->start() <= start
+ && (p->second->start() + static_cast<off_t>(p->second->size())
+ >= start + static_cast<off_t>(size)))
+ {
+ if (byteshift == -1U || byteshift == p->second->byteshift())
+ {
+ p->second->set_accessed();
+ return p->second;
+ }
- if (p->second->start() + static_cast<off_t>(p->second->size())
- < start + static_cast<off_t>(size))
- return NULL;
+ if (vshifted != NULL && *vshifted == NULL)
+ *vshifted = p->second;
+ }
- p->second->set_accessed();
+ ++p;
+ }
- return p->second;
+ return NULL;
}
// Read SIZE bytes from the file starting at offset START. Read into
void
File_read::read(off_t start, section_size_type size, void* p) const
{
- const File_read::View* pv = this->find_view(start, size);
+ const File_read::View* pv = this->find_view(start, size, -1U, NULL);
if (pv != NULL)
{
- memcpy(p, pv->data() + (start - pv->start()), size);
+ memcpy(p, pv->data() + (start - pv->start() + pv->byteshift()), size);
return;
}
this->do_read(start, size, p);
}
-// Find an existing view or make a new one.
+// Add a new view. There may already be an existing view at this
+// offset. If there is, the new view will be larger, and should
+// replace the old view.
-File_read::View*
-File_read::find_or_make_view(off_t start, section_size_type size, bool cache)
+void
+File_read::add_view(File_read::View* v)
{
- gold_assert(!this->token_.is_writable());
- this->released_ = false;
+ std::pair<Views::iterator, bool> ins =
+ this->views_.insert(std::make_pair(std::make_pair(v->start(),
+ v->byteshift()),
+ v));
+ if (ins.second)
+ return;
- File_read::View* v = this->find_view(start, size);
- if (v != NULL)
+ // There was an existing view at this offset. It must not be large
+ // enough. We can't delete it here, since something might be using
+ // it; we put it on a list to be deleted when the file is unlocked.
+ File_read::View* vold = ins.first->second;
+ gold_assert(vold->size() < v->size());
+ if (vold->should_cache())
{
- if (cache)
- v->set_cache();
- return v;
+ v->set_cache();
+ vold->clear_cache();
}
+ this->saved_views_.push_back(vold);
- 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));
+ ins.first->second = v;
+}
- if (!ins.second)
- {
- // There was an existing view at this offset. It must not be
- // large enough. We can't delete it here, since something might
- // be using it; put it on a list to be deleted when the file is
- // unlocked.
- v = ins.first->second;
- gold_assert(v->size() - (start - v->start()) < size);
- if (v->should_cache())
- cache = true;
- v->clear_cache();
- this->saved_views_.push_back(v);
- }
+// Make a new view with a specified byteshift, reading the data from
+// the file.
- // We need to map data from the file.
+File_read::View*
+File_read::make_view(off_t start, section_size_type size,
+ unsigned int byteshift, bool cache)
+{
+ off_t poff = File_read::page_offset(start);
section_size_type psize = File_read::pages(size + (start - poff));
gold_assert(psize >= size);
}
- if (this->contents_ != NULL)
+ File_read::View* v;
+ if (this->contents_ != NULL || byteshift != 0)
{
- unsigned char* p = new unsigned char[psize];
- this->do_read(poff, psize, p);
- v = new File_read::View(poff, psize, p, cache, false);
+ unsigned char* p = new unsigned char[psize + byteshift];
+ memset(p, 0, byteshift);
+ this->do_read(poff, psize, p + byteshift);
+ v = new File_read::View(poff, psize, p, byteshift, cache, false);
}
else
{
this->mapped_bytes_ += psize;
const unsigned char* pbytes = static_cast<const unsigned char*>(p);
- v = new File_read::View(poff, psize, pbytes, cache, true);
+ v = new File_read::View(poff, psize, pbytes, 0, cache, true);
}
- ins.first->second = v;
+ this->add_view(v);
+
return v;
}
+// Find a View or make a new one, shifted as required by the file
+// offset OFFSET and ALIGNED.
+
+File_read::View*
+File_read::find_or_make_view(off_t offset, off_t start,
+ section_size_type size, bool aligned, bool cache)
+{
+ unsigned int byteshift;
+ if (offset == 0)
+ byteshift = 0;
+ else
+ {
+ unsigned int target_size = (!parameters->target_valid()
+ ? 64
+ : parameters->target().get_size());
+ byteshift = offset & ((target_size / 8) - 1);
+
+ // Set BYTESHIFT to the number of dummy bytes which must be
+ // inserted before the data in order for this data to be
+ // aligned.
+ if (byteshift != 0)
+ byteshift = (target_size / 8) - byteshift;
+ }
+
+ // Try to find a View with the required BYTESHIFT.
+ File_read::View* vshifted;
+ File_read::View* v = this->find_view(offset + start, size,
+ aligned ? byteshift : -1U,
+ &vshifted);
+ if (v != NULL)
+ {
+ if (cache)
+ v->set_cache();
+ return v;
+ }
+
+ // If VSHIFTED is not NULL, then it has the data we need, but with
+ // the wrong byteshift.
+ v = vshifted;
+ if (v != NULL)
+ {
+ gold_assert(aligned);
+
+ unsigned char* pbytes = new unsigned char[v->size() + byteshift];
+ memset(pbytes, 0, byteshift);
+ memcpy(pbytes + byteshift, v->data() + v->byteshift(), v->size());
+
+ File_read::View* shifted_view = new File_read::View(v->start(), v->size(),
+ pbytes, byteshift,
+ cache, false);
+
+ this->add_view(shifted_view);
+ return shifted_view;
+ }
+
+ // Make a new view. If we don't need an aligned view, use a
+ // byteshift of 0, so that we can use mmap.
+ return this->make_view(offset + start, size,
+ aligned ? byteshift : 0,
+ cache);
+}
+
// Get a view into the file.
const unsigned char*
-File_read::get_view(off_t start, section_size_type size, bool cache)
+File_read::get_view(off_t offset, off_t start, section_size_type size,
+ bool aligned, bool cache)
{
- File_read::View* pv = this->find_or_make_view(start, size, cache);
- return pv->data() + (start - pv->start());
+ File_read::View* pv = this->find_or_make_view(offset, start, size,
+ aligned, cache);
+ return pv->data() + (offset + start - pv->start() + pv->byteshift());
}
File_view*
-File_read::get_lasting_view(off_t start, section_size_type size, bool cache)
+File_read::get_lasting_view(off_t offset, off_t start, section_size_type size,
+ bool aligned, bool cache)
{
- File_read::View* pv = this->find_or_make_view(start, size, cache);
+ File_read::View* pv = this->find_or_make_view(offset, start, size,
+ aligned, cache);
pv->lock();
- return new File_view(*this, pv, pv->data() + (start - pv->start()));
+ return new File_view(*this, pv,
+ (pv->data()
+ + (offset + start - pv->start() + pv->byteshift())));
}
// Use readv to read COUNT entries from RM starting at START. BASE
else
{
File_read::View* view = this->find_view(base + i_off,
- end_off - i_off);
+ end_off - i_off,
+ -1U, NULL);
if (view == NULL)
this->do_readv(base, rm, i, j - i);
else
{
const unsigned char* v = (view->data()
- + (base + i_off - view->start()));
+ + (base + i_off - view->start()
+ + view->byteshift()));
for (size_t k = i; k < j; ++k)
{
const Read_multiple_entry& k_entry(rm[k]);
{ return this->size_; }
// Return a view into the file starting at file offset START for
- // SIZE bytes. The pointer will remain valid until the File_read is
- // unlocked. It is an error if we can not read enough data from the
- // file. The CACHE parameter is a hint as to whether it will be
+ // SIZE bytes. OFFSET is the offset into the input file for the
+ // file we are reading; this is zero for a normal object file,
+ // non-zero for an object file in an archive. ALIGNED is true if
+ // the data must be naturally aligned; this only matters when OFFSET
+ // is not zero. The pointer will remain valid until the File_read
+ // is unlocked. It is an error if we can not read enough data from
+ // the file. The CACHE parameter is a hint as to whether it will be
// useful to cache this data for later accesses--i.e., later calls
// to get_view, read, or get_lasting_view which retrieve the same
// data.
const unsigned char*
- get_view(off_t start, section_size_type size, bool cache);
+ get_view(off_t offset, off_t start, section_size_type size, bool aligned,
+ bool cache);
// Read data from the file into the buffer P starting at file offset
// START for SIZE bytes.
// for SIZE bytes. This is allocated with new, and the caller is
// responsible for deleting it when done. The data associated with
// this view will remain valid until the view is deleted. It is an
- // error if we can not read enough data from the file. The CACHE
- // parameter is as in get_view.
+ // error if we can not read enough data from the file. The OFFSET,
+ // ALIGNED and CACHE parameters are as in get_view.
File_view*
- get_lasting_view(off_t start, section_size_type size, bool cache);
+ get_lasting_view(off_t offset, off_t start, section_size_type size,
+ bool aligned, bool cache);
// Mark all views as no longer cached.
void
clear_view_cache_marks();
+ // Discard all uncached views. This is normally done by release(),
+ // but not for objects in archives. FIXME: This is a complicated
+ // interface, and it would be nice to have something more automatic.
+ void
+ clear_uncached_views()
+ { this->clear_views(false); }
+
// A struct used to do a multiple read.
struct Read_multiple_entry
{
{
public:
View(off_t start, section_size_type size, const unsigned char* data,
- bool cache, bool mapped)
+ unsigned int byteshift, bool cache, bool mapped)
: start_(start), size_(size), data_(data), lock_count_(0),
- cache_(cache), mapped_(mapped), accessed_(true)
+ byteshift_(byteshift), cache_(cache), mapped_(mapped), accessed_(true)
{ }
~View();
bool
is_locked();
+ unsigned int
+ byteshift() const
+ { return this->byteshift_; }
+
void
set_cache()
{ this->cache_ = true; }
View(const View&);
View& operator=(const View&);
+ // The file offset of the start of the view.
off_t start_;
+ // The size of the view.
section_size_type size_;
+ // A pointer to the actual bytes.
const unsigned char* data_;
+ // The number of locks on this view.
int lock_count_;
+ // The number of bytes that the view is shifted relative to the
+ // underlying file. This is used to align data. This is normally
+ // zero, except possibly for an object in an archive.
+ unsigned int byteshift_;
+ // Whether the view is cached.
bool cache_;
+ // Whether the view is mapped into memory. If not, data_ points
+ // to memory allocated using new[].
bool mapped_;
+ // Whether the view has been accessed recently.
bool accessed_;
};
friend class View;
friend class File_view;
+ // The type of a mapping from page start and byte shift to views.
+ typedef std::map<std::pair<off_t, unsigned int>, View*> Views;
+
+ // A simple list of Views.
+ typedef std::list<View*> Saved_views;
+
// Find a view into the file.
View*
- find_view(off_t start, section_size_type size) const;
+ find_view(off_t start, section_size_type size, unsigned int byteshift,
+ View** vshifted) const;
// Read data from the file into a buffer.
void
do_read(off_t start, section_size_type size, void* p) const;
+ // Add a view.
+ void
+ add_view(View*);
+
+ // Make a view into the file.
+ View*
+ make_view(off_t start, section_size_type size, unsigned int byteshift,
+ bool cache);
+
// Find or make a view into the file.
View*
- find_or_make_view(off_t start, section_size_type size, bool cache);
+ find_or_make_view(off_t offset, off_t start, section_size_type size,
+ bool aligned, bool cache);
// Clear the file views.
void
pages(off_t file_size)
{ return (file_size + (page_size - 1)) & ~ (page_size - 1); }
- // The type of a mapping from page start to views.
- typedef std::map<off_t, View*> Views;
-
- // A simple list of Views.
- typedef std::list<View*> Saved_views;
-
// The maximum number of entries we will pass to ::readv.
static const size_t max_readv_entries = 128;
{
Location loc(this->do_section_contents(shndx));
*plen = convert_to_section_size_type(loc.data_size);
- return this->get_view(loc.file_offset, *plen, cache);
+ return this->get_view(loc.file_offset, *plen, true, cache);
}
// Read the section data into SD. This is code common to Sized_relobj
// Read the section headers.
const off_t shoff = elf_file->shoff();
const unsigned int shnum = this->shnum();
- sd->section_headers = this->get_lasting_view(shoff, shnum * shdr_size, true);
+ sd->section_headers = this->get_lasting_view(shoff, shnum * shdr_size,
+ true, true);
// Read the section names.
const unsigned char* pshdrs = sd->section_headers->data();
sd->section_names_size =
convert_to_section_size_type(shdrnames.get_sh_size());
sd->section_names = this->get_lasting_view(shdrnames.get_sh_offset(),
- sd->section_names_size, false);
+ sd->section_names_size, false,
+ false);
}
// If NAME is the name of a special .gnu.warning section, arrange for
off_t readoff = this->has_eh_frame_ ? dataoff : extoff;
section_size_type readsize = this->has_eh_frame_ ? datasize : extsize;
- File_view* fvsymtab = this->get_lasting_view(readoff, readsize, false);
+ File_view* fvsymtab = this->get_lasting_view(readoff, readsize, true, false);
// Read the section header for the symbol names.
unsigned int strtab_shndx = symtabshdr.get_sh_link();
// Read the symbol names.
File_view* fvstrtab = this->get_lasting_view(strtabshdr.get_sh_offset(),
- strtabshdr.get_sh_size(), true);
+ strtabshdr.get_sh_size(),
+ false, true);
sd->symbols = fvsymtab;
sd->symbols_size = readsize;
{
// Read the section contents.
const unsigned char* pcon = this->get_view(shdr.get_sh_offset(),
- shdr.get_sh_size(), false);
+ shdr.get_sh_size(), true, false);
const elfcpp::Elf_Word* pword =
reinterpret_cast<const elfcpp::Elf_Word*>(pcon);
return false;
}
off_t symoff = symshdr.get_sh_offset() + shdr.get_sh_info() * This::sym_size;
- const unsigned char* psym = this->get_view(symoff, This::sym_size, false);
+ const unsigned char* psym = this->get_view(symoff, This::sym_size, true,
+ false);
elfcpp::Sym<size, big_endian> sym(psym);
// Read the symbol table names.
gold_assert(loccount == symtabshdr.get_sh_info());
off_t locsize = loccount * sym_size;
const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(),
- locsize, true);
+ locsize, true, true);
// Read the symbol names.
const unsigned int strtab_shndx = symtabshdr.get_sh_link();
const int sym_size = This::sym_size;
off_t locsize = loccount * sym_size;
const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(),
- locsize, false);
+ locsize, true, false);
// Read the symbol names.
const unsigned int strtab_shndx = symtabshdr.get_sh_link();
// Return a View.
View
view(off_t file_offset, section_size_type data_size)
- { return View(this->get_view(file_offset, data_size, true)); }
+ { return View(this->get_view(file_offset, data_size, true, true)); }
// Report an error.
void
// Get a View given a Location.
View view(Location loc)
- { return View(this->get_view(loc.file_offset, loc.data_size, true)); }
+ { return View(this->get_view(loc.file_offset, loc.data_size, true, true)); }
// Get a view into the underlying file.
const unsigned char*
- get_view(off_t start, section_size_type size, bool cache)
+ get_view(off_t start, section_size_type size, bool aligned, bool cache)
{
- return this->input_file()->file().get_view(start + this->offset_, size,
- cache);
+ return this->input_file()->file().get_view(this->offset_, start, size,
+ aligned, cache);
}
// Get a lasting view into the underlying file.
File_view*
- get_lasting_view(off_t start, section_size_type size, bool cache)
+ get_lasting_view(off_t start, section_size_type size, bool aligned,
+ bool cache)
{
- return this->input_file()->file().get_lasting_view(start + this->offset_,
- size, cache);
+ return this->input_file()->file().get_lasting_view(this->offset_, start,
+ size, aligned, cache);
}
// Read data from the underlying file.
const unsigned char *pshdrs = this->get_view(this->elf_file_.shoff(),
shnum * This::shdr_size,
- true);
+ true, true);
// Skip the first, dummy, section.
const unsigned char *ps = pshdrs + This::shdr_size;
for (unsigned int i = 1; i < shnum; ++i, ps += This::shdr_size)
sr.reloc_shndx = i;
sr.data_shndx = shndx;
sr.contents = this->get_lasting_view(shdr.get_sh_offset(), sh_size,
- true);
+ true, true);
sr.sh_type = sh_type;
sr.reloc_count = reloc_count;
sr.output_section = os;
gold_assert(loccount == symtabshdr.get_sh_info());
off_t locsize = loccount * sym_size;
rd->local_symbols = this->get_lasting_view(symtabshdr.get_sh_offset(),
- locsize, true);
+ locsize, true, true);
}
}
// Read the section headers.
const unsigned char* pshdrs = this->get_view(this->elf_file_.shoff(),
shnum * This::shdr_size,
- true);
+ true, true);
Views views;
views.resize(shnum);
off_t sh_size = shdr.get_sh_size();
const unsigned char* prelocs = this->get_view(shdr.get_sh_offset(),
- sh_size, false);
+ sh_size, true, false);
unsigned int reloc_size;
if (sh_type == elfcpp::SHT_REL)