Reduce the number of system calls. Use readv instead of pread. Do
better handling of cached views.
This commit is contained in:
parent
2745d86e69
commit
cb29561284
@ -85,7 +85,8 @@ Archive::setup(Task* task)
|
||||
// The first member of the archive should be the symbol table.
|
||||
std::string armap_name;
|
||||
section_size_type armap_size =
|
||||
convert_to_section_size_type(this->read_header(sarmag, &armap_name));
|
||||
convert_to_section_size_type(this->read_header(sarmag, false,
|
||||
&armap_name));
|
||||
off_t off = sarmag;
|
||||
if (armap_name.empty())
|
||||
{
|
||||
@ -96,15 +97,18 @@ Archive::setup(Task* task)
|
||||
gold_error(_("%s: no archive symbol table (run ranlib)"),
|
||||
this->name().c_str());
|
||||
|
||||
// See if there is an extended name table.
|
||||
// See if there is an extended name table. We cache these views
|
||||
// because it is likely that we will want to read the following
|
||||
// header in the add_symbols routine.
|
||||
if ((off & 1) != 0)
|
||||
++off;
|
||||
std::string xname;
|
||||
off_t extended_size = this->read_header(off, &xname);
|
||||
section_size_type extended_size =
|
||||
convert_to_section_size_type(this->read_header(off, true, &xname));
|
||||
if (xname == "/")
|
||||
{
|
||||
const unsigned char* p = this->get_view(off + sizeof(Archive_header),
|
||||
extended_size, false);
|
||||
extended_size, true);
|
||||
const char* px = reinterpret_cast<const char*>(p);
|
||||
this->extended_names_.assign(px, extended_size);
|
||||
}
|
||||
@ -157,9 +161,9 @@ Archive::read_armap(off_t start, section_size_type size)
|
||||
// of the member.
|
||||
|
||||
off_t
|
||||
Archive::read_header(off_t off, std::string* pname)
|
||||
Archive::read_header(off_t off, bool cache, std::string* pname)
|
||||
{
|
||||
const unsigned char* p = this->get_view(off, sizeof(Archive_header), false);
|
||||
const unsigned char* p = this->get_view(off, sizeof(Archive_header), cache);
|
||||
const Archive_header* hdr = reinterpret_cast<const Archive_header*>(p);
|
||||
return this->interpret_header(hdr, off, pname);
|
||||
}
|
||||
@ -370,7 +374,7 @@ Archive::include_member(Symbol_table* symtab, Layout* layout,
|
||||
Input_objects* input_objects, off_t off)
|
||||
{
|
||||
std::string n;
|
||||
this->read_header(off, &n);
|
||||
this->read_header(off, false, &n);
|
||||
|
||||
const off_t memoff = off + static_cast<off_t>(sizeof(Archive_header));
|
||||
|
||||
|
@ -117,10 +117,11 @@ class Archive
|
||||
void
|
||||
read_armap(off_t start, section_size_type size);
|
||||
|
||||
// Read an archive member header at OFF. Return the size of the
|
||||
// member, and set *PNAME to the name.
|
||||
// Read an archive member header at OFF. CACHE is whether to cache
|
||||
// the file view. Return the size of the member, and set *PNAME to
|
||||
// the name.
|
||||
off_t
|
||||
read_header(off_t off, std::string* pname);
|
||||
read_header(off_t off, bool cache, std::string* pname);
|
||||
|
||||
// Interpret an archive header HDR at OFF. Return the size of the
|
||||
// member, and set *PNAME to the name.
|
||||
|
@ -335,7 +335,7 @@ Sized_dynobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
|
||||
|
||||
sd->symbol_names = this->get_lasting_view(strtabshdr.get_sh_offset(),
|
||||
strtabshdr.get_sh_size(),
|
||||
true);
|
||||
false);
|
||||
sd->symbol_names_size =
|
||||
convert_to_section_size_type(strtabshdr.get_sh_size());
|
||||
|
||||
@ -667,6 +667,10 @@ Sized_dynobj<size, big_endian>::do_add_symbols(Symbol_table* symtab,
|
||||
delete sd->verneed;
|
||||
sd->verneed = NULL;
|
||||
}
|
||||
|
||||
// This is normally the last time we will read any data from this
|
||||
// file.
|
||||
this->clear_view_cache_marks();
|
||||
}
|
||||
|
||||
// Given a vector of hash codes, compute the number of hash buckets to
|
||||
|
212
gold/fileread.cc
212
gold/fileread.cc
@ -27,6 +27,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/uio.h>
|
||||
#include "filenames.h"
|
||||
|
||||
#include "options.h"
|
||||
@ -194,11 +195,21 @@ inline File_read::View*
|
||||
File_read::find_view(off_t start, section_size_type size) const
|
||||
{
|
||||
off_t page = File_read::page_offset(start);
|
||||
Views::const_iterator p = this->views_.find(page);
|
||||
if (p == this->views_.end())
|
||||
return NULL;
|
||||
if (p->second->size() - (start - page) < size)
|
||||
|
||||
Views::const_iterator p = this->views_.lower_bound(page);
|
||||
if (p == this->views_.end() || p->first > page)
|
||||
{
|
||||
if (p == this->views_.begin())
|
||||
return NULL;
|
||||
--p;
|
||||
}
|
||||
|
||||
if (p->second->start() + static_cast<off_t>(p->second->size())
|
||||
< start + static_cast<off_t>(size))
|
||||
return NULL;
|
||||
|
||||
p->second->set_accessed();
|
||||
|
||||
return p->second;
|
||||
}
|
||||
|
||||
@ -244,7 +255,7 @@ File_read::do_read(off_t start, section_size_type size, void* p) const
|
||||
void
|
||||
File_read::read(off_t start, section_size_type size, void* p) const
|
||||
{
|
||||
File_read::View* pv = this->find_view(start, size);
|
||||
const File_read::View* pv = this->find_view(start, size);
|
||||
if (pv != NULL)
|
||||
{
|
||||
memcpy(p, pv->data() + (start - pv->start()), size);
|
||||
@ -262,6 +273,14 @@ File_read::find_or_make_view(off_t start, section_size_type size, bool cache)
|
||||
gold_assert(!this->token_.is_writable());
|
||||
this->released_ = false;
|
||||
|
||||
File_read::View* v = this->find_view(start, size);
|
||||
if (v != NULL)
|
||||
{
|
||||
if (cache)
|
||||
v->set_cache();
|
||||
return v;
|
||||
}
|
||||
|
||||
off_t poff = File_read::page_offset(start);
|
||||
|
||||
File_read::View* const vnull = NULL;
|
||||
@ -270,21 +289,19 @@ File_read::find_or_make_view(off_t start, section_size_type size, bool cache)
|
||||
|
||||
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 (cache)
|
||||
v->set_cache();
|
||||
return v;
|
||||
}
|
||||
|
||||
// This view is not large enough.
|
||||
// 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);
|
||||
}
|
||||
|
||||
// We need to read data from the file. We read full pages for
|
||||
// greater efficiency on small files.
|
||||
// We need to map data from the file.
|
||||
|
||||
section_size_type psize = File_read::pages(size + (start - poff));
|
||||
|
||||
@ -294,8 +311,6 @@ File_read::find_or_make_view(off_t start, section_size_type size, bool cache)
|
||||
gold_assert(psize >= size);
|
||||
}
|
||||
|
||||
File_read::View* v;
|
||||
|
||||
if (this->contents_ != NULL)
|
||||
{
|
||||
unsigned char* p = new unsigned char[psize];
|
||||
@ -304,7 +319,7 @@ File_read::find_or_make_view(off_t start, section_size_type size, bool cache)
|
||||
}
|
||||
else
|
||||
{
|
||||
void* p = ::mmap(NULL, psize, PROT_READ, MAP_SHARED,
|
||||
void* p = ::mmap(NULL, psize, PROT_READ, MAP_PRIVATE,
|
||||
this->descriptor_, poff);
|
||||
if (p == MAP_FAILED)
|
||||
gold_fatal(_("%s: mmap offset %lld size %lld failed: %s"),
|
||||
@ -340,7 +355,143 @@ File_read::get_lasting_view(off_t start, section_size_type size, bool cache)
|
||||
return new File_view(*this, pv, pv->data() + (start - pv->start()));
|
||||
}
|
||||
|
||||
// Remove all the file views.
|
||||
// Use readv to read COUNT entries from RM starting at START. BASE
|
||||
// must be added to all file offsets in RM.
|
||||
|
||||
void
|
||||
File_read::do_readv(off_t base, const Read_multiple& rm, size_t start,
|
||||
size_t count)
|
||||
{
|
||||
unsigned char discard[File_read::page_size];
|
||||
iovec iov[File_read::max_readv_entries * 2];
|
||||
size_t iov_index = 0;
|
||||
|
||||
off_t first_offset = rm[start].file_offset;
|
||||
off_t last_offset = first_offset;
|
||||
ssize_t want = 0;
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
const Read_multiple_entry& i_entry(rm[start + i]);
|
||||
|
||||
if (i_entry.file_offset > last_offset)
|
||||
{
|
||||
size_t skip = i_entry.file_offset - last_offset;
|
||||
gold_assert(skip <= sizeof discard);
|
||||
|
||||
iov[iov_index].iov_base = discard;
|
||||
iov[iov_index].iov_len = skip;
|
||||
++iov_index;
|
||||
|
||||
want += skip;
|
||||
}
|
||||
|
||||
iov[iov_index].iov_base = i_entry.buffer;
|
||||
iov[iov_index].iov_len = i_entry.size;
|
||||
++iov_index;
|
||||
|
||||
want += i_entry.size;
|
||||
|
||||
last_offset = i_entry.file_offset + i_entry.size;
|
||||
}
|
||||
|
||||
gold_assert(iov_index < sizeof iov / sizeof iov[0]);
|
||||
|
||||
if (::lseek(this->descriptor_, base + first_offset, SEEK_SET) < 0)
|
||||
gold_fatal(_("%s: lseek failed: %s"),
|
||||
this->filename().c_str(), strerror(errno));
|
||||
|
||||
ssize_t got = ::readv(this->descriptor_, iov, iov_index);
|
||||
|
||||
if (got < 0)
|
||||
gold_fatal(_("%s: readv failed: %s"),
|
||||
this->filename().c_str(), strerror(errno));
|
||||
if (got != want)
|
||||
gold_fatal(_("%s: file too short: read only %zd of %zd bytes at %lld"),
|
||||
this->filename().c_str(),
|
||||
got, want, static_cast<long long>(base + first_offset));
|
||||
}
|
||||
|
||||
// Read several pieces of data from the file.
|
||||
|
||||
void
|
||||
File_read::read_multiple(off_t base, const Read_multiple& rm)
|
||||
{
|
||||
size_t count = rm.size();
|
||||
size_t i = 0;
|
||||
while (i < count)
|
||||
{
|
||||
// Find up to MAX_READV_ENTRIES consecutive entries which are
|
||||
// less than one page apart.
|
||||
const Read_multiple_entry& i_entry(rm[i]);
|
||||
off_t i_off = i_entry.file_offset;
|
||||
off_t end_off = i_off + i_entry.size;
|
||||
size_t j;
|
||||
for (j = i + 1; j < count; ++j)
|
||||
{
|
||||
if (j - i >= File_read::max_readv_entries)
|
||||
break;
|
||||
const Read_multiple_entry& j_entry(rm[j]);
|
||||
off_t j_off = j_entry.file_offset;
|
||||
gold_assert(j_off >= end_off);
|
||||
off_t j_end_off = j_off + j_entry.size;
|
||||
if (j_end_off - end_off >= File_read::page_size)
|
||||
break;
|
||||
end_off = j_end_off;
|
||||
}
|
||||
|
||||
if (j == i + 1)
|
||||
this->read(base + i_off, i_entry.size, i_entry.buffer);
|
||||
else
|
||||
{
|
||||
File_read::View* view = this->find_view(base + i_off,
|
||||
end_off - i_off);
|
||||
if (view == NULL)
|
||||
this->do_readv(base, rm, i, j - i);
|
||||
else
|
||||
{
|
||||
const unsigned char* v = (view->data()
|
||||
+ (base + i_off - view->start()));
|
||||
for (size_t k = i; k < j; ++k)
|
||||
{
|
||||
const Read_multiple_entry& k_entry(rm[k]);
|
||||
gold_assert(k_entry.file_offset - i_off + k_entry.size
|
||||
<= end_off - i_off);
|
||||
memcpy(k_entry.buffer,
|
||||
v + (k_entry.file_offset - i_off),
|
||||
k_entry.size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
|
||||
// Mark all views as no longer cached.
|
||||
|
||||
void
|
||||
File_read::clear_view_cache_marks()
|
||||
{
|
||||
// Just ignore this if there are multiple objects associated with
|
||||
// the file. Otherwise we will wind up uncaching and freeing some
|
||||
// views for other objects.
|
||||
if (this->object_count_ > 1)
|
||||
return;
|
||||
|
||||
for (Views::iterator p = this->views_.begin();
|
||||
p != this->views_.end();
|
||||
++p)
|
||||
p->second->clear_cache();
|
||||
for (Saved_views::iterator p = this->saved_views_.begin();
|
||||
p != this->saved_views_.end();
|
||||
++p)
|
||||
(*p)->clear_cache();
|
||||
}
|
||||
|
||||
// Remove all the file views. For a file which has multiple
|
||||
// associated objects (i.e., an archive), we keep accessed views
|
||||
// around until next time, in the hopes that they will be useful for
|
||||
// the next object.
|
||||
|
||||
void
|
||||
File_read::clear_views(bool destroying)
|
||||
@ -348,8 +499,19 @@ File_read::clear_views(bool destroying)
|
||||
Views::iterator p = this->views_.begin();
|
||||
while (p != this->views_.end())
|
||||
{
|
||||
if (!p->second->is_locked()
|
||||
&& (destroying || !p->second->should_cache()))
|
||||
bool should_delete;
|
||||
if (p->second->is_locked())
|
||||
should_delete = false;
|
||||
else if (destroying)
|
||||
should_delete = true;
|
||||
else if (p->second->should_cache())
|
||||
should_delete = false;
|
||||
else if (this->object_count_ > 1 && p->second->accessed())
|
||||
should_delete = false;
|
||||
else
|
||||
should_delete = true;
|
||||
|
||||
if (should_delete)
|
||||
{
|
||||
delete p->second;
|
||||
|
||||
@ -362,6 +524,7 @@ File_read::clear_views(bool destroying)
|
||||
else
|
||||
{
|
||||
gold_assert(!destroying);
|
||||
p->second->clear_accessed();
|
||||
++p;
|
||||
}
|
||||
}
|
||||
@ -369,8 +532,7 @@ File_read::clear_views(bool destroying)
|
||||
Saved_views::iterator q = this->saved_views_.begin();
|
||||
while (q != this->saved_views_.end())
|
||||
{
|
||||
if (!(*q)->is_locked()
|
||||
&& (destroying || !(*q)->should_cache()))
|
||||
if (!(*q)->is_locked())
|
||||
{
|
||||
delete *q;
|
||||
q = this->saved_views_.erase(q);
|
||||
|
@ -46,8 +46,9 @@ class File_read
|
||||
{
|
||||
public:
|
||||
File_read()
|
||||
: name_(), descriptor_(-1), size_(0), token_(false), views_(),
|
||||
saved_views_(), contents_(NULL), mapped_bytes_(0), released_(true)
|
||||
: name_(), descriptor_(-1), object_count_(0), size_(0), token_(false),
|
||||
views_(), saved_views_(), contents_(NULL), mapped_bytes_(0),
|
||||
released_(true)
|
||||
{ }
|
||||
|
||||
~File_read();
|
||||
@ -68,6 +69,16 @@ class File_read
|
||||
filename() const
|
||||
{ return this->name_; }
|
||||
|
||||
// Add an object associated with a file.
|
||||
void
|
||||
add_object()
|
||||
{ ++this->object_count_; }
|
||||
|
||||
// Remove an object associated with a file.
|
||||
void
|
||||
remove_object()
|
||||
{ --this->object_count_; }
|
||||
|
||||
// Lock the file for exclusive access within a particular Task::run
|
||||
// execution. This means that the descriptor can not be closed.
|
||||
// This routine may only be called when the workqueue lock is held.
|
||||
@ -126,6 +137,34 @@ class File_read
|
||||
File_view*
|
||||
get_lasting_view(off_t start, section_size_type size, bool cache);
|
||||
|
||||
// Mark all views as no longer cached.
|
||||
void
|
||||
clear_view_cache_marks();
|
||||
|
||||
// A struct used to do a multiple read.
|
||||
struct Read_multiple_entry
|
||||
{
|
||||
// The file offset of the data to read.
|
||||
off_t file_offset;
|
||||
// The amount of data to read.
|
||||
section_size_type size;
|
||||
// The buffer where the data should be placed.
|
||||
unsigned char* buffer;
|
||||
|
||||
Read_multiple_entry(off_t o, section_size_type s, unsigned char* b)
|
||||
: file_offset(o), size(s), buffer(b)
|
||||
{ }
|
||||
};
|
||||
|
||||
typedef std::vector<Read_multiple_entry> Read_multiple;
|
||||
|
||||
// Read a bunch of data from the file into various different
|
||||
// locations. The vector must be sorted by ascending file_offset.
|
||||
// BASE is a base offset to be added to all the offsets in the
|
||||
// vector.
|
||||
void
|
||||
read_multiple(off_t base, const Read_multiple&);
|
||||
|
||||
// Dump statistical information to stderr.
|
||||
static void
|
||||
print_stats();
|
||||
@ -154,7 +193,7 @@ class File_read
|
||||
View(off_t start, section_size_type size, const unsigned char* data,
|
||||
bool cache, bool mapped)
|
||||
: start_(start), size_(size), data_(data), lock_count_(0),
|
||||
cache_(cache), mapped_(mapped)
|
||||
cache_(cache), mapped_(mapped), accessed_(true)
|
||||
{ }
|
||||
|
||||
~View();
|
||||
@ -184,10 +223,26 @@ class File_read
|
||||
set_cache()
|
||||
{ this->cache_ = true; }
|
||||
|
||||
void
|
||||
clear_cache()
|
||||
{ this->cache_ = false; }
|
||||
|
||||
bool
|
||||
should_cache() const
|
||||
{ return this->cache_; }
|
||||
|
||||
void
|
||||
set_accessed()
|
||||
{ this->accessed_ = true; }
|
||||
|
||||
void
|
||||
clear_accessed()
|
||||
{ this->accessed_= false; }
|
||||
|
||||
bool
|
||||
accessed() const
|
||||
{ return this->accessed_; }
|
||||
|
||||
private:
|
||||
View(const View&);
|
||||
View& operator=(const View&);
|
||||
@ -198,6 +253,7 @@ class File_read
|
||||
int lock_count_;
|
||||
bool cache_;
|
||||
bool mapped_;
|
||||
bool accessed_;
|
||||
};
|
||||
|
||||
friend class View;
|
||||
@ -238,10 +294,20 @@ class File_read
|
||||
// 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;
|
||||
|
||||
// Use readv to read data.
|
||||
void
|
||||
do_readv(off_t base, const Read_multiple&, size_t start, size_t count);
|
||||
|
||||
// File name.
|
||||
std::string name_;
|
||||
// File descriptor.
|
||||
int descriptor_;
|
||||
// The number of objects associated with this file. This will be
|
||||
// more than 1 in the case of an archive.
|
||||
int object_count_;
|
||||
// File size.
|
||||
off_t size_;
|
||||
// A token used to lock the file.
|
||||
|
@ -739,7 +739,7 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab,
|
||||
off = this->set_section_offsets(off, BEFORE_INPUT_SECTIONS_PASS);
|
||||
|
||||
// Create the symbol table sections.
|
||||
this->create_symtab_sections(input_objects, symtab, task, &off);
|
||||
this->create_symtab_sections(input_objects, symtab, &off);
|
||||
if (!parameters->doing_static_link())
|
||||
this->assign_local_dynsym_offsets(input_objects);
|
||||
|
||||
@ -1212,7 +1212,6 @@ Layout::count_local_symbols(const Task* task,
|
||||
void
|
||||
Layout::create_symtab_sections(const Input_objects* input_objects,
|
||||
Symbol_table* symtab,
|
||||
const Task* task,
|
||||
off_t* poff)
|
||||
{
|
||||
int symsize;
|
||||
@ -1286,7 +1285,7 @@ Layout::create_symtab_sections(const Input_objects* input_objects,
|
||||
== this->dynsym_section_->data_size() - locsize);
|
||||
}
|
||||
|
||||
off = symtab->finalize(task, local_symcount, off, dynoff, dyn_global_index,
|
||||
off = symtab->finalize(local_symcount, off, dynoff, dyn_global_index,
|
||||
dyncount, &this->sympool_);
|
||||
|
||||
if (!parameters->strip_all())
|
||||
|
@ -292,8 +292,7 @@ class Layout
|
||||
|
||||
// Create the output sections for the symbol table.
|
||||
void
|
||||
create_symtab_sections(const Input_objects*, Symbol_table*, const Task*,
|
||||
off_t*);
|
||||
create_symtab_sections(const Input_objects*, Symbol_table*, off_t*);
|
||||
|
||||
// Create the .shstrtab section.
|
||||
Output_section*
|
||||
|
@ -125,7 +125,17 @@ Object::handle_gnu_warning_section(const char* name, unsigned int shndx,
|
||||
const int warn_prefix_len = sizeof warn_prefix - 1;
|
||||
if (strncmp(name, warn_prefix, warn_prefix_len) == 0)
|
||||
{
|
||||
symtab->add_warning(name + warn_prefix_len, this, shndx);
|
||||
// Read the section contents to get the warning text. It would
|
||||
// be nicer if we only did this if we have to actually issue a
|
||||
// warning. Unfortunately, warnings are issued as we relocate
|
||||
// sections. That means that we can not lock the object then,
|
||||
// as we might try to issue the same warning multiple times
|
||||
// simultaneously.
|
||||
section_size_type len;
|
||||
const unsigned char* contents = this->section_contents(shndx, &len,
|
||||
false);
|
||||
std::string warning(reinterpret_cast<const char*>(contents), len);
|
||||
symtab->add_warning(name + warn_prefix_len, this, warning);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -404,7 +414,7 @@ Sized_relobj<size, big_endian>::include_section_group(
|
||||
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, true);
|
||||
const unsigned char* psym = this->get_view(symoff, This::sym_size, false);
|
||||
elfcpp::Sym<size, big_endian> sym(psym);
|
||||
|
||||
// Read the symbol table names.
|
||||
@ -729,10 +739,11 @@ Sized_relobj<size, big_endian>::do_add_symbols(Symbol_table* symtab,
|
||||
sd->symbol_names = NULL;
|
||||
}
|
||||
|
||||
// Finalize the local symbols. Here we add their names to *POOL and
|
||||
// *DYNPOOL, and we add their values to THIS->LOCAL_VALUES_. This
|
||||
// function is always called from a singleton thread. The actual
|
||||
// output of the local symbols will occur in a separate task.
|
||||
// First pass over the local symbols. Here we add their names to
|
||||
// *POOL and *DYNPOOL, and we store the symbol value in
|
||||
// THIS->LOCAL_VALUES_. This function is always called from a
|
||||
// singleton thread. This is followed by a call to
|
||||
// finalize_local_symbols.
|
||||
|
||||
template<int size, bool big_endian>
|
||||
void
|
||||
@ -833,7 +844,7 @@ Sized_relobj<size, big_endian>::do_count_local_symbols(Stringpool* pool,
|
||||
this->output_local_dynsym_count_ = dyncount;
|
||||
}
|
||||
|
||||
// Finalize the local symbols. Here we add their values to
|
||||
// Finalize the local symbols. Here we set the final value in
|
||||
// THIS->LOCAL_VALUES_ and set their output symbol table indexes.
|
||||
// This function is always called from a singleton thread. The actual
|
||||
// output of the local symbols will occur in a separate task.
|
||||
@ -1008,7 +1019,7 @@ Sized_relobj<size, big_endian>::write_local_symbols(
|
||||
section_size_type strtab_size;
|
||||
const unsigned char* pnamesu = this->section_contents(strtab_shndx,
|
||||
&strtab_size,
|
||||
true);
|
||||
false);
|
||||
const char* pnames = reinterpret_cast<const char*>(pnamesu);
|
||||
|
||||
// Get views into the output file for the portions of the symbol table
|
||||
|
@ -139,10 +139,10 @@ class Object
|
||||
off_t offset = 0)
|
||||
: name_(name), input_file_(input_file), offset_(offset), shnum_(-1U),
|
||||
is_dynamic_(is_dynamic), target_(NULL)
|
||||
{ }
|
||||
{ input_file->file().add_object(); }
|
||||
|
||||
virtual ~Object()
|
||||
{ }
|
||||
{ this->input_file_->file().remove_object(); }
|
||||
|
||||
// Return the name of the object as we would report it to the tuser.
|
||||
const std::string&
|
||||
@ -294,6 +294,37 @@ class Object
|
||||
View view(Location loc)
|
||||
{ return View(this->get_view(loc.file_offset, loc.data_size, true)); }
|
||||
|
||||
// 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 + this->offset_, size,
|
||||
cache);
|
||||
}
|
||||
|
||||
// Get a lasting view into the underlying file.
|
||||
File_view*
|
||||
get_lasting_view(off_t start, section_size_type size, bool cache)
|
||||
{
|
||||
return this->input_file()->file().get_lasting_view(start + this->offset_,
|
||||
size, cache);
|
||||
}
|
||||
|
||||
// Read data from the underlying file.
|
||||
void
|
||||
read(off_t start, section_size_type size, void* p) const
|
||||
{ this->input_file()->file().read(start + this->offset_, size, p); }
|
||||
|
||||
// Read multiple data from the underlying file.
|
||||
void
|
||||
read_multiple(const File_read::Read_multiple& rm)
|
||||
{ this->input_file()->file().read_multiple(this->offset_, rm); }
|
||||
|
||||
// Stop caching views in the underlying file.
|
||||
void
|
||||
clear_view_cache_marks()
|
||||
{ this->input_file()->file().clear_view_cache_marks(); }
|
||||
|
||||
protected:
|
||||
// Read the symbols--implemented by child class.
|
||||
virtual void
|
||||
@ -342,27 +373,6 @@ class Object
|
||||
input_file() const
|
||||
{ return this->input_file_; }
|
||||
|
||||
// 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 + this->offset_, size,
|
||||
cache);
|
||||
}
|
||||
|
||||
// Get a lasting view into the underlying file.
|
||||
File_view*
|
||||
get_lasting_view(off_t start, section_size_type size, bool cache)
|
||||
{
|
||||
return this->input_file()->file().get_lasting_view(start + this->offset_,
|
||||
size, cache);
|
||||
}
|
||||
|
||||
// Read data from the underlying file.
|
||||
void
|
||||
read(off_t start, section_size_type size, void* p) const
|
||||
{ this->input_file()->file().read(start + this->offset_, size, p); }
|
||||
|
||||
// Set the target.
|
||||
void
|
||||
set_target(int machine, int size, bool big_endian, int osabi,
|
||||
@ -1206,7 +1216,7 @@ class Sized_relobj : public Relobj
|
||||
// Write section data to the output file. Record the views and
|
||||
// sizes in VIEWS for use when relocating.
|
||||
void
|
||||
write_sections(const unsigned char* pshdrs, Output_file*, Views*) const;
|
||||
write_sections(const unsigned char* pshdrs, Output_file*, Views*);
|
||||
|
||||
// Relocate the sections in the output file.
|
||||
void
|
||||
@ -1229,6 +1239,15 @@ class Sized_relobj : public Relobj
|
||||
const Stringpool_template<char>*,
|
||||
const Stringpool_template<char>*);
|
||||
|
||||
// Clear the local symbol information.
|
||||
void
|
||||
clear_local_symbols()
|
||||
{
|
||||
this->local_values_.clear();
|
||||
this->local_got_offsets_.clear();
|
||||
this->local_tls_got_offsets_.clear();
|
||||
}
|
||||
|
||||
// The GOT offsets of local symbols. This map also stores GOT offsets
|
||||
// for tp-relative offsets for TLS symbols.
|
||||
typedef Unordered_map<unsigned int, unsigned int> Local_got_offsets;
|
||||
|
@ -22,6 +22,8 @@
|
||||
|
||||
#include "gold.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "workqueue.h"
|
||||
#include "symtab.h"
|
||||
#include "output.h"
|
||||
@ -159,6 +161,11 @@ Relocate_task::run(Workqueue*)
|
||||
{
|
||||
this->object_->relocate(this->options_, this->symtab_, this->layout_,
|
||||
this->of_);
|
||||
|
||||
// This is normally the last thing we will do with an object, so
|
||||
// uncache all views.
|
||||
this->object_->clear_view_cache_marks();
|
||||
|
||||
this->object_->release();
|
||||
}
|
||||
|
||||
@ -376,8 +383,20 @@ Sized_relobj<size, big_endian>::do_relocate(const General_options& options,
|
||||
|
||||
// Write out the local symbols.
|
||||
this->write_local_symbols(of, layout->sympool(), layout->dynpool());
|
||||
|
||||
// We should no longer need the local symbol values.
|
||||
this->clear_local_symbols();
|
||||
}
|
||||
|
||||
// Sort a Read_multiple vector by file offset.
|
||||
struct Read_multiple_compare
|
||||
{
|
||||
inline bool
|
||||
operator()(const File_read::Read_multiple_entry& rme1,
|
||||
const File_read::Read_multiple_entry& rme2) const
|
||||
{ return rme1.file_offset < rme2.file_offset; }
|
||||
};
|
||||
|
||||
// Write section data to the output file. PSHDRS points to the
|
||||
// section headers. Record the views in *PVIEWS for use when
|
||||
// relocating.
|
||||
@ -386,11 +405,14 @@ template<int size, bool big_endian>
|
||||
void
|
||||
Sized_relobj<size, big_endian>::write_sections(const unsigned char* pshdrs,
|
||||
Output_file* of,
|
||||
Views* pviews) const
|
||||
Views* pviews)
|
||||
{
|
||||
unsigned int shnum = this->shnum();
|
||||
const std::vector<Map_to_output>& map_sections(this->map_to_output());
|
||||
|
||||
File_read::Read_multiple rm;
|
||||
bool is_sorted = true;
|
||||
|
||||
const unsigned char* p = pshdrs + This::shdr_size;
|
||||
for (unsigned int i = 1; i < shnum; ++i, p += This::shdr_size)
|
||||
{
|
||||
@ -468,7 +490,13 @@ Sized_relobj<size, big_endian>::write_sections(const unsigned char* pshdrs,
|
||||
unsigned char* buffer = os->postprocessing_buffer();
|
||||
view = buffer + view_start;
|
||||
if (output_offset != -1)
|
||||
this->read(shdr.get_sh_offset(), view_size, view);
|
||||
{
|
||||
off_t sh_offset = shdr.get_sh_offset();
|
||||
if (!rm.empty() && rm.back().file_offset > sh_offset)
|
||||
is_sorted = false;
|
||||
rm.push_back(File_read::Read_multiple_entry(sh_offset,
|
||||
view_size, view));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -477,7 +505,11 @@ Sized_relobj<size, big_endian>::write_sections(const unsigned char* pshdrs,
|
||||
else
|
||||
{
|
||||
view = of->get_output_view(view_start, view_size);
|
||||
this->read(shdr.get_sh_offset(), view_size, view);
|
||||
off_t sh_offset = shdr.get_sh_offset();
|
||||
if (!rm.empty() && rm.back().file_offset > sh_offset)
|
||||
is_sorted = false;
|
||||
rm.push_back(File_read::Read_multiple_entry(sh_offset,
|
||||
view_size, view));
|
||||
}
|
||||
}
|
||||
|
||||
@ -490,6 +522,14 @@ Sized_relobj<size, big_endian>::write_sections(const unsigned char* pshdrs,
|
||||
pvs->is_input_output_view = output_offset == -1;
|
||||
pvs->is_postprocessing_view = os->requires_postprocessing();
|
||||
}
|
||||
|
||||
// Actually read the data.
|
||||
if (!rm.empty())
|
||||
{
|
||||
if (!is_sorted)
|
||||
std::sort(rm.begin(), rm.end(), Read_multiple_compare());
|
||||
this->read_multiple(rm);
|
||||
}
|
||||
}
|
||||
|
||||
// Relocate section data. VIEWS points to the section data as views
|
||||
|
@ -1403,8 +1403,8 @@ Symbol_table::set_dynsym_indexes(const Target* target,
|
||||
// OFF. Add their names to POOL. Return the new file offset.
|
||||
|
||||
off_t
|
||||
Symbol_table::finalize(const Task* task, unsigned int index, off_t off,
|
||||
off_t dynoff, size_t dyn_global_index, size_t dyncount,
|
||||
Symbol_table::finalize(unsigned int index, off_t off, off_t dynoff,
|
||||
size_t dyn_global_index, size_t dyncount,
|
||||
Stringpool* pool)
|
||||
{
|
||||
off_t ret;
|
||||
@ -1437,7 +1437,7 @@ Symbol_table::finalize(const Task* task, unsigned int index, off_t off,
|
||||
|
||||
// Now that we have the final symbol table, we can reliably note
|
||||
// which symbols should get warnings.
|
||||
this->warnings_.note_warnings(this, task);
|
||||
this->warnings_.note_warnings(this);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -2004,10 +2004,10 @@ Symbol_table::detect_odr_violations(const Task* task,
|
||||
|
||||
void
|
||||
Warnings::add_warning(Symbol_table* symtab, const char* name, Object* obj,
|
||||
unsigned int shndx)
|
||||
const std::string& warning)
|
||||
{
|
||||
name = symtab->canonicalize_name(name);
|
||||
this->warnings_[name].set(obj, shndx);
|
||||
this->warnings_[name].set(obj, warning);
|
||||
}
|
||||
|
||||
// Look through the warnings and mark the symbols for which we should
|
||||
@ -2015,7 +2015,7 @@ Warnings::add_warning(Symbol_table* symtab, const char* name, Object* obj,
|
||||
// sources for all the symbols.
|
||||
|
||||
void
|
||||
Warnings::note_warnings(Symbol_table* symtab, const Task* task)
|
||||
Warnings::note_warnings(Symbol_table* symtab)
|
||||
{
|
||||
for (Warning_table::iterator p = this->warnings_.begin();
|
||||
p != this->warnings_.end();
|
||||
@ -2025,24 +2025,7 @@ Warnings::note_warnings(Symbol_table* symtab, const Task* task)
|
||||
if (sym != NULL
|
||||
&& sym->source() == Symbol::FROM_OBJECT
|
||||
&& sym->object() == p->second.object)
|
||||
{
|
||||
sym->set_has_warning();
|
||||
|
||||
// Read the section contents to get the warning text. It
|
||||
// would be nicer if we only did this if we have to actually
|
||||
// issue a warning. Unfortunately, warnings are issued as
|
||||
// we relocate sections. That means that we can not lock
|
||||
// the object then, as we might try to issue the same
|
||||
// warning multiple times simultaneously.
|
||||
{
|
||||
Task_lock_obj<Object> tl(task, p->second.object);
|
||||
const unsigned char* c;
|
||||
section_size_type len;
|
||||
c = p->second.object->section_contents(p->second.shndx, &len,
|
||||
false);
|
||||
p->second.set_text(reinterpret_cast<const char*>(c), len);
|
||||
}
|
||||
}
|
||||
sym->set_has_warning();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -897,15 +897,16 @@ class Warnings
|
||||
: warnings_()
|
||||
{ }
|
||||
|
||||
// Add a warning for symbol NAME in section SHNDX in object OBJ.
|
||||
// Add a warning for symbol NAME in object OBJ. WARNING is the text
|
||||
// of the warning.
|
||||
void
|
||||
add_warning(Symbol_table* symtab, const char* name, Object* obj,
|
||||
unsigned int shndx);
|
||||
const std::string& warning);
|
||||
|
||||
// For each symbol for which we should give a warning, make a note
|
||||
// on the symbol.
|
||||
void
|
||||
note_warnings(Symbol_table* symtab, const Task*);
|
||||
note_warnings(Symbol_table* symtab);
|
||||
|
||||
// Issue a warning for a reference to SYM at RELINFO's location.
|
||||
template<int size, bool big_endian>
|
||||
@ -922,25 +923,19 @@ class Warnings
|
||||
{
|
||||
// The object the warning is in.
|
||||
Object* object;
|
||||
// The index of the warning section.
|
||||
unsigned int shndx;
|
||||
// The warning text if we have already loaded it.
|
||||
// The warning text.
|
||||
std::string text;
|
||||
|
||||
Warning_location()
|
||||
: object(NULL), shndx(0), text()
|
||||
: object(NULL), text()
|
||||
{ }
|
||||
|
||||
void
|
||||
set(Object* o, unsigned int s)
|
||||
set(Object* o, const std::string& t)
|
||||
{
|
||||
this->object = o;
|
||||
this->shndx = s;
|
||||
this->text = t;
|
||||
}
|
||||
|
||||
void
|
||||
set_text(const char* t, section_size_type l)
|
||||
{ this->text.assign(t, l); }
|
||||
};
|
||||
|
||||
// A mapping from warning symbol names (canonicalized in
|
||||
@ -1057,10 +1052,11 @@ class Symbol_table
|
||||
void
|
||||
allocate_commons(const General_options&, Layout*);
|
||||
|
||||
// Add a warning for symbol NAME in section SHNDX in object OBJ.
|
||||
// Add a warning for symbol NAME in object OBJ. WARNING is the text
|
||||
// of the warning.
|
||||
void
|
||||
add_warning(const char* name, Object* obj, unsigned int shndx)
|
||||
{ this->warnings_.add_warning(this, name, obj, shndx); }
|
||||
add_warning(const char* name, Object* obj, const std::string& warning)
|
||||
{ this->warnings_.add_warning(this, name, obj, warning); }
|
||||
|
||||
// Canonicalize a symbol name for use in the hash table.
|
||||
const char*
|
||||
@ -1103,7 +1099,7 @@ class Symbol_table
|
||||
// symbol, and DYNCOUNT is the number of global dynamic symbols.
|
||||
// This records the parameters, and returns the new file offset.
|
||||
off_t
|
||||
finalize(const Task*, unsigned int index, off_t off, off_t dynoff,
|
||||
finalize(unsigned int index, off_t off, off_t dynoff,
|
||||
size_t dyn_global_index, size_t dyncount, Stringpool* pool);
|
||||
|
||||
// Write out the global symbols.
|
||||
|
Loading…
x
Reference in New Issue
Block a user