Rework stringpool and hash tables so that we always generate the same

output regardless of randomize_va_space.
This commit is contained in:
Ian Lance Taylor
2006-11-07 04:40:46 +00:00
parent 7c8fe5c480
commit f0641a0b38
8 changed files with 152 additions and 78 deletions

View File

@ -53,7 +53,7 @@ Layout::Layout(const General_options& options)
size_t size_t
Layout::Hash_key::operator()(const Layout::Key& k) const Layout::Hash_key::operator()(const Layout::Key& k) const
{ {
return reinterpret_cast<size_t>(k.first) + k.second.first + k.second.second; return k.first + k.second.first + k.second.second;
} }
// Whether to include this section in the link. // Whether to include this section in the link.
@ -95,7 +95,7 @@ Layout::find_output_section(const char* name) const
for (Section_name_map::const_iterator p = this->section_name_map_.begin(); for (Section_name_map::const_iterator p = this->section_name_map_.begin();
p != this->section_name_map_.end(); p != this->section_name_map_.end();
++p) ++p)
if (strcmp(p->first.first, name) == 0) if (strcmp(p->second->name(), name) == 0)
return p->second; return p->second;
return NULL; return NULL;
} }
@ -121,15 +121,15 @@ Layout::find_output_segment(elfcpp::PT type, elfcpp::Elf_Word set,
// and section flags FLAGS. // and section flags FLAGS.
Output_section* Output_section*
Layout::get_output_section(const char* name, elfcpp::Elf_Word type, Layout::get_output_section(const char* name, Stringpool::Key name_key,
elfcpp::Elf_Xword flags) elfcpp::Elf_Word type, elfcpp::Elf_Xword flags)
{ {
// We should ignore some flags. // We should ignore some flags.
flags &= ~ (elfcpp::SHF_INFO_LINK flags &= ~ (elfcpp::SHF_INFO_LINK
| elfcpp::SHF_LINK_ORDER | elfcpp::SHF_LINK_ORDER
| elfcpp::SHF_GROUP); | elfcpp::SHF_GROUP);
const Key key(name, std::make_pair(type, flags)); const Key key(name_key, std::make_pair(type, flags));
const std::pair<Key, Output_section*> v(key, NULL); const std::pair<Key, Output_section*> v(key, NULL);
std::pair<Section_name_map::iterator, bool> ins( std::pair<Section_name_map::iterator, bool> ins(
this->section_name_map_.insert(v)); this->section_name_map_.insert(v));
@ -167,11 +167,13 @@ Layout::layout(Relobj* object, unsigned int shndx, const char* name,
// FIXME: Handle SHF_OS_NONCONFORMING here. // FIXME: Handle SHF_OS_NONCONFORMING here.
// Canonicalize the section name. // Canonicalize the section name.
name = this->namepool_.add(name, len); Stringpool::Key name_key;
name = this->namepool_.add(name, len, &name_key);
// Find the output section. The output section is selected based on // Find the output section. The output section is selected based on
// the section name, type, and flags. // the section name, type, and flags.
Output_section* os = this->get_output_section(name, shdr.get_sh_type(), Output_section* os = this->get_output_section(name, name_key,
shdr.get_sh_type(),
shdr.get_sh_flags()); shdr.get_sh_flags());
// FIXME: Handle SHF_LINK_ORDER somewhere. // FIXME: Handle SHF_LINK_ORDER somewhere.
@ -189,9 +191,10 @@ Layout::add_output_section_data(const char* name, elfcpp::Elf_Word type,
Output_section_data* posd) Output_section_data* posd)
{ {
// Canonicalize the name. // Canonicalize the name.
name = this->namepool_.add(name); Stringpool::Key name_key;
name = this->namepool_.add(name, &name_key);
Output_section* os = this->get_output_section(name, type, flags); Output_section* os = this->get_output_section(name, name_key, type, flags);
os->add_output_section_data(posd); os->add_output_section_data(posd);
} }
@ -672,12 +675,12 @@ Layout::create_symtab_sections(int size, const Input_objects* input_objects,
this->sympool_.set_string_offsets(); this->sympool_.set_string_offsets();
const char* symtab_name = this->namepool_.add(".symtab"); const char* symtab_name = this->namepool_.add(".symtab", NULL);
Output_section* osymtab = new Output_section_symtab(symtab_name, Output_section* osymtab = new Output_section_symtab(symtab_name,
off - startoff); off - startoff);
this->section_list_.push_back(osymtab); this->section_list_.push_back(osymtab);
const char* strtab_name = this->namepool_.add(".strtab"); const char* strtab_name = this->namepool_.add(".strtab", NULL);
Output_section *ostrtab = new Output_section_strtab(strtab_name, Output_section *ostrtab = new Output_section_strtab(strtab_name,
&this->sympool_); &this->sympool_);
this->section_list_.push_back(ostrtab); this->section_list_.push_back(ostrtab);
@ -703,7 +706,7 @@ Layout::create_shstrtab()
// FIXME: We don't need to create a .shstrtab section if we are // FIXME: We don't need to create a .shstrtab section if we are
// stripping everything. // stripping everything.
const char* name = this->namepool_.add(".shstrtab"); const char* name = this->namepool_.add(".shstrtab", NULL);
this->namepool_.set_string_offsets(); this->namepool_.set_string_offsets();

View File

@ -196,8 +196,8 @@ class Layout
// Return the output section for NAME, TYPE and FLAGS. // Return the output section for NAME, TYPE and FLAGS.
Output_section* Output_section*
get_output_section(const char* name, elfcpp::Elf_Word type, get_output_section(const char* name, Stringpool::Key name_key,
elfcpp::Elf_Xword flags); elfcpp::Elf_Word type, elfcpp::Elf_Xword flags);
// Create a new Output_section. // Create a new Output_section.
Output_section* Output_section*
@ -218,7 +218,7 @@ class Layout
// Mapping from input section name/type/flags to output section. We // Mapping from input section name/type/flags to output section. We
// use canonicalized strings here. // use canonicalized strings here.
typedef std::pair<const char*, typedef std::pair<Stringpool::Key,
std::pair<elfcpp::Elf_Word, elfcpp::Elf_Xword> > Key; std::pair<elfcpp::Elf_Word, elfcpp::Elf_Xword> > Key;
struct Hash_key struct Hash_key

View File

@ -628,7 +628,7 @@ Sized_relobj<size, big_endian>::do_finalize_local_symbols(off_t off,
if (sym.get_st_type() != elfcpp::STT_SECTION) if (sym.get_st_type() != elfcpp::STT_SECTION)
{ {
pool->add(pnames + sym.get_st_name()); pool->add(pnames + sym.get_st_name(), NULL);
off += sym_size; off += sym_size;
++count; ++count;
} }

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2006-11-06 13:40-0800\n" "POT-Creation-Date: 2006-11-06 15:58-0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -477,22 +477,22 @@ msgstr ""
msgid "%s: %s: unsupported symbol binding %d for symbol %s\n" msgid "%s: %s: unsupported symbol binding %d for symbol %s\n"
msgstr "" msgstr ""
#: symtab.cc:432 #: symtab.cc:440
#, c-format #, c-format
msgid "%s: %s: mixing 32-bit and 64-bit ELF objects\n" msgid "%s: %s: mixing 32-bit and 64-bit ELF objects\n"
msgstr "" msgstr ""
#: symtab.cc:449 #: symtab.cc:457
#, c-format #, c-format
msgid "%s: %s: bad global symbol name offset %u at %lu\n" msgid "%s: %s: bad global symbol name offset %u at %lu\n"
msgstr "" msgstr ""
#: symtab.cc:865 symtab.cc:1004 #: symtab.cc:882 symtab.cc:1021
#, c-format #, c-format
msgid "%s: %s: unsupported symbol section 0x%x\n" msgid "%s: %s: unsupported symbol section 0x%x\n"
msgstr "" msgstr ""
#: symtab.cc:1114 #: symtab.cc:1131
#, c-format #, c-format
msgid "%s: %s: warning: %s\n" msgid "%s: %s: warning: %s\n"
msgstr "" msgstr ""

View File

@ -14,7 +14,7 @@ namespace gold
{ {
Stringpool::Stringpool() Stringpool::Stringpool()
: string_set_(), strings_(), strtab_size_(0) : string_set_(), strings_(), strtab_size_(0), next_index_(1)
{ {
} }
@ -55,12 +55,18 @@ Stringpool::Stringpool_hash::operator()(const char* s) const
} }
// Add a string to the list of canonical strings. Return a pointer to // Add a string to the list of canonical strings. Return a pointer to
// the canonical string. // the canonical string. If PKEY is not NULL, set *PKEY to the key.
const char* const char*
Stringpool::add_string(const char* s) Stringpool::add_string(const char* s, Key* pkey)
{ {
// The size we allocate for a new Stringdata.
const size_t buffer_size = 1000; const size_t buffer_size = 1000;
// The amount we multiply the Stringdata index when calculating the
// key.
const size_t key_mult = 1024;
assert(key_mult >= buffer_size);
size_t len = strlen(s); size_t len = strlen(s);
size_t alc; size_t alc;
@ -81,7 +87,12 @@ Stringpool::add_string(const char* s)
{ {
char* ret = psd->data + psd->len; char* ret = psd->data + psd->len;
memcpy(ret, s, len + 1); memcpy(ret, s, len + 1);
if (pkey != NULL)
*pkey = psd->index * key_mult + psd->len;
psd->len += len + 1; psd->len += len + 1;
return ret; return ret;
} }
} }
@ -90,17 +101,24 @@ Stringpool::add_string(const char* s)
psd->alc = alc - sizeof(Stringdata); psd->alc = alc - sizeof(Stringdata);
memcpy(psd->data, s, len + 1); memcpy(psd->data, s, len + 1);
psd->len = len + 1; psd->len = len + 1;
psd->index = this->next_index_;
++this->next_index_;
if (pkey != NULL)
*pkey = psd->index * key_mult;
if (front) if (front)
this->strings_.push_front(psd); this->strings_.push_front(psd);
else else
this->strings_.push_back(psd); this->strings_.push_back(psd);
return psd->data; return psd->data;
} }
// Add a string to a string pool. // Add a string to a string pool.
const char* const char*
Stringpool::add(const char* s) Stringpool::add(const char* s, Key* pkey)
{ {
// FIXME: This will look up the entry twice in the hash table. The // FIXME: This will look up the entry twice in the hash table. The
// problem is that we can't insert S before we canonicalize it. I // problem is that we can't insert S before we canonicalize it. I
@ -110,33 +128,48 @@ Stringpool::add(const char* s)
String_set_type::const_iterator p = this->string_set_.find(s); String_set_type::const_iterator p = this->string_set_.find(s);
if (p != this->string_set_.end()) if (p != this->string_set_.end())
{
if (pkey != NULL)
*pkey = p->second.first;
return p->first; return p->first;
}
const char* ret = this->add_string(s); Key k;
std::pair<const char*, off_t> val(ret, 0); const char* ret = this->add_string(s, &k);
const off_t ozero = 0;
std::pair<const char*, Val> element(ret, std::make_pair(k, ozero));
std::pair<String_set_type::iterator, bool> ins = std::pair<String_set_type::iterator, bool> ins =
this->string_set_.insert(val); this->string_set_.insert(element);
assert(ins.second); assert(ins.second);
if (pkey != NULL)
*pkey = k;
return ret; return ret;
} }
// Add a prefix of a string to a string pool. // Add a prefix of a string to a string pool.
const char* const char*
Stringpool::add(const char* s, size_t len) Stringpool::add(const char* s, size_t len, Key* pkey)
{ {
// FIXME: This implementation should be rewritten when we rewrite // FIXME: This implementation should be rewritten when we rewrite
// the hash table to avoid copying. // the hash table to avoid copying.
std::string st(s, len); std::string st(s, len);
return this->add(st); return this->add(st, pkey);
} }
const char* const char*
Stringpool::find(const char* s) const Stringpool::find(const char* s, Key* pkey) const
{ {
String_set_type::const_iterator p = this->string_set_.find(s); String_set_type::const_iterator p = this->string_set_.find(s);
if (p == this->string_set_.end()) if (p == this->string_set_.end())
return NULL; return NULL;
if (pkey != NULL)
*pkey = p->second.first;
return p->first; return p->first;
} }
@ -206,14 +239,14 @@ Stringpool::set_string_offsets()
for (size_t i = 0; i < count; ++i) for (size_t i = 0; i < count; ++i)
{ {
if (v[i]->first[0] == '\0') if (v[i]->first[0] == '\0')
v[i]->second = 0; v[i]->second.second = 0;
else if (i > 0 && Stringpool::is_suffix(v[i]->first, v[i - 1]->first)) else if (i > 0 && Stringpool::is_suffix(v[i]->first, v[i - 1]->first))
v[i]->second = (v[i - 1]->second v[i]->second.second = (v[i - 1]->second.second
+ strlen(v[i - 1]->first) + strlen(v[i - 1]->first)
- strlen(v[i]->first)); - strlen(v[i]->first));
else else
{ {
v[i]->second = offset; v[i]->second.second = offset;
offset += strlen(v[i]->first) + 1; offset += strlen(v[i]->first) + 1;
} }
} }
@ -229,7 +262,7 @@ Stringpool::get_offset(const char* s) const
{ {
String_set_type::const_iterator p = this->string_set_.find(s); String_set_type::const_iterator p = this->string_set_.find(s);
if (p != this->string_set_.end()) if (p != this->string_set_.end())
return p->second; return p->second.second;
abort(); abort();
} }
@ -244,7 +277,7 @@ Stringpool::write(Output_file* of, off_t offset)
for (String_set_type::const_iterator p = this->string_set_.begin(); for (String_set_type::const_iterator p = this->string_set_.begin();
p != this->string_set_.end(); p != this->string_set_.end();
++p) ++p)
strcpy(view + p->second, p->first); strcpy(view + p->second.second, p->first);
of->write_output_view(offset, this->strtab_size_, viewu); of->write_output_view(offset, this->strtab_size_, viewu);
} }

View File

@ -17,34 +17,42 @@ class Output_file;
class Stringpool class Stringpool
{ {
public: public:
// The type of a key into the stringpool. A key value will always
// be the same during any run of the linker. The string pointers
// may change when using address space randomization. We use key
// values in order to get repeatable runs when the value is inserted
// into an unordered hash table. Zero is never a valid key.
typedef size_t Key;
Stringpool(); Stringpool();
~Stringpool(); ~Stringpool();
// Add a string to the pool. This returns a canonical permanent // Add a string to the pool. This returns a canonical permanent
// pointer to the string. // pointer to the string. If PKEY is not NULL, this sets *PKEY to
// the key for the string.
const char* const char*
add(const char*); add(const char*, Key* pkey);
const char* const char*
add(const std::string& s) add(const std::string& s, Key* pkey)
{ return this->add(s.c_str()); } { return this->add(s.c_str(), pkey); }
// Add the prefix of a string to the pool. // Add the prefix of a string to the pool.
const char* const char*
add(const char *, size_t); add(const char *, size_t, Key* pkey);
// If a string is present, return the canonical string. Otherwise, // If a string is present, return the canonical string. Otherwise,
// return NULL. // return NULL. If PKEY is not NULL, set *PKEY to the key.
const char* const char*
find(const char*) const; find(const char*, Key* pkey) const;
// Turn the stringpool into an ELF strtab: determine the offsets of // Turn the stringpool into an ELF strtab: determine the offsets of
// all the strings. // all the strings.
void void
set_string_offsets(); set_string_offsets();
// Get the offset of a string. // Get the offset of a string in an ELF strtab.
off_t off_t
get_offset(const char*) const; get_offset(const char*) const;
@ -72,13 +80,15 @@ class Stringpool
size_t len; size_t len;
// Allocated size of buffer. // Allocated size of buffer.
size_t alc; size_t alc;
// Buffer index.
unsigned int index;
// Buffer. // Buffer.
char data[1]; char data[1];
}; };
// Copy a string into the buffers, returning a canonical string. // Copy a string into the buffers, returning a canonical string.
const char* const char*
add_string(const char*); add_string(const char*, Key*);
struct Stringpool_hash struct Stringpool_hash
{ {
@ -96,16 +106,19 @@ class Stringpool
// Return whether s1 is a suffix of s2. // Return whether s1 is a suffix of s2.
static bool is_suffix(const char* s1, const char* s2); static bool is_suffix(const char* s1, const char* s2);
// The hash table is a map from string names to offsets. We only // The hash table is a map from string names to a pair of Key and
// use the offsets if we turn this into an ELF strtab section. // ELF strtab offsets. We only use the offsets if we turn this into
// an ELF strtab section.
typedef std::pair<Key, off_t> Val;
#ifdef HAVE_TR1_UNORDERED_SET #ifdef HAVE_TR1_UNORDERED_SET
typedef Unordered_map<const char*, off_t, Stringpool_hash, typedef Unordered_map<const char*, Val, Stringpool_hash,
Stringpool_eq, Stringpool_eq,
std::allocator<std::pair<const char* const, off_t> >, std::allocator<std::pair<const char* const, Val> >,
true> String_set_type; true> String_set_type;
#else #else
typedef Unordered_map<const char*, off_t, Stringpool_hash, typedef Unordered_map<const char*, Val, Stringpool_hash,
Stringpool_eq> String_set_type; Stringpool_eq> String_set_type;
#endif #endif
@ -118,9 +131,17 @@ class Stringpool
String_set_type::iterator) const; String_set_type::iterator) const;
}; };
// List of Stringdata structures.
typedef std::list<Stringdata*> Stringdata_list;
// Mapping from const char* to namepool entry.
String_set_type string_set_; String_set_type string_set_;
std::list<Stringdata*> strings_; // List of buffers.
Stringdata_list strings_;
// Size of ELF strtab.
off_t strtab_size_; off_t strtab_size_;
// Next Stringdata index.
unsigned int next_index_;
}; };
} // End namespace gold. } // End namespace gold.

View File

@ -174,8 +174,7 @@ Symbol_table::~Symbol_table()
size_t size_t
Symbol_table::Symbol_table_hash::operator()(const Symbol_table_key& key) const Symbol_table::Symbol_table_hash::operator()(const Symbol_table_key& key) const
{ {
return (reinterpret_cast<size_t>(key.first) return key.first ^ key.second;
^ reinterpret_cast<size_t>(key.second));
} }
// The symbol table key equality function. This is only called with // The symbol table key equality function. This is only called with
@ -216,17 +215,20 @@ Symbol_table::resolve_forwards(Symbol* from) const
Symbol* Symbol*
Symbol_table::lookup(const char* name, const char* version) const Symbol_table::lookup(const char* name, const char* version) const
{ {
name = this->namepool_.find(name); Stringpool::Key name_key;
name = this->namepool_.find(name, &name_key);
if (name == NULL) if (name == NULL)
return NULL; return NULL;
Stringpool::Key version_key = 0;
if (version != NULL) if (version != NULL)
{ {
version = this->namepool_.find(version); version = this->namepool_.find(version, &version_key);
if (version == NULL) if (version == NULL)
return NULL; return NULL;
} }
Symbol_table_key key(name, version); Symbol_table_key key(name_key, version_key);
Symbol_table::Symbol_table_type::const_iterator p = this->table_.find(key); Symbol_table::Symbol_table_type::const_iterator p = this->table_.find(key);
if (p == this->table_.end()) if (p == this->table_.end())
return NULL; return NULL;
@ -282,19 +284,24 @@ template<int size, bool big_endian>
Symbol* Symbol*
Symbol_table::add_from_object(Object* object, Symbol_table::add_from_object(Object* object,
const char *name, const char *name,
const char *version, bool def, Stringpool::Key name_key,
const char *version,
Stringpool::Key version_key,
bool def,
const elfcpp::Sym<size, big_endian>& sym) const elfcpp::Sym<size, big_endian>& sym)
{ {
Symbol* const snull = NULL; Symbol* const snull = NULL;
std::pair<typename Symbol_table_type::iterator, bool> ins = std::pair<typename Symbol_table_type::iterator, bool> ins =
this->table_.insert(std::make_pair(std::make_pair(name, version), snull)); this->table_.insert(std::make_pair(std::make_pair(name_key, version_key),
snull));
std::pair<typename Symbol_table_type::iterator, bool> insdef = std::pair<typename Symbol_table_type::iterator, bool> insdef =
std::make_pair(this->table_.end(), false); std::make_pair(this->table_.end(), false);
if (def) if (def)
{ {
const char* const vnull = NULL; const Stringpool::Key vnull_key = 0;
insdef = this->table_.insert(std::make_pair(std::make_pair(name, vnull), insdef = this->table_.insert(std::make_pair(std::make_pair(name_key,
vnull_key),
snull)); snull));
} }
@ -379,7 +386,8 @@ Symbol_table::add_from_object(Object* object,
{ {
this->table_.erase(insdef.first); this->table_.erase(insdef.first);
// Inserting insdef invalidated ins. // Inserting insdef invalidated ins.
this->table_.erase(std::make_pair(name, version)); this->table_.erase(std::make_pair(name_key,
version_key));
} }
return NULL; return NULL;
} }
@ -477,12 +485,16 @@ Symbol_table::add_from_object(
Symbol* res; Symbol* res;
if (ver == NULL) if (ver == NULL)
{ {
name = this->namepool_.add(name); Stringpool::Key name_key;
res = this->add_from_object(object, name, NULL, false, *psym); name = this->namepool_.add(name, &name_key);
res = this->add_from_object(object, name, name_key, NULL, 0,
false, *psym);
} }
else else
{ {
name = this->namepool_.add(name, ver - name); Stringpool::Key name_key;
name = this->namepool_.add(name, ver - name, &name_key);
bool def = false; bool def = false;
++ver; ++ver;
if (*ver == '@') if (*ver == '@')
@ -490,8 +502,12 @@ Symbol_table::add_from_object(
def = true; def = true;
++ver; ++ver;
} }
ver = this->namepool_.add(ver);
res = this->add_from_object(object, name, ver, def, *psym); Stringpool::Key ver_key;
ver = this->namepool_.add(ver, &ver_key);
res = this->add_from_object(object, name, name_key, ver, ver_key,
def, *psym);
} }
*sympointers++ = res; *sympointers++ = res;
@ -525,12 +541,13 @@ Symbol_table::define_special_symbol(Target* target, const char* name,
else else
{ {
// Canonicalize NAME. // Canonicalize NAME.
name = this->namepool_.add(name); Stringpool::Key name_key;
name = this->namepool_.add(name, &name_key);
Symbol* const snull = NULL; Symbol* const snull = NULL;
const char* const vnull = NULL; const Stringpool::Key ver_key = 0;
std::pair<typename Symbol_table_type::iterator, bool> ins = std::pair<typename Symbol_table_type::iterator, bool> ins =
this->table_.insert(std::make_pair(std::make_pair(name, vnull), this->table_.insert(std::make_pair(std::make_pair(name_key, ver_key),
snull)); snull));
if (!ins.second) if (!ins.second)
@ -937,7 +954,7 @@ Symbol_table::sized_finalize(off_t off, Stringpool* pool)
} }
sym->set_value(value); sym->set_value(value);
pool->add(sym->name()); pool->add(sym->name(), NULL);
++count; ++count;
off += sym_size; off += sym_size;
++p; ++p;

View File

@ -683,7 +683,7 @@ class Symbol_table
// Canonicalize a symbol name for use in the hash table. // Canonicalize a symbol name for use in the hash table.
const char* const char*
canonicalize_name(const char* name) canonicalize_name(const char* name)
{ return this->namepool_.add(name); } { return this->namepool_.add(name, NULL); }
// Possibly issue a warning for a reference to SYM at LOCATION which // Possibly issue a warning for a reference to SYM at LOCATION which
// is in OBJ. // is in OBJ.
@ -718,9 +718,9 @@ class Symbol_table
// Add a symbol. // Add a symbol.
template<int size, bool big_endian> template<int size, bool big_endian>
Symbol* Symbol*
add_from_object(Object*, const char *name, add_from_object(Object*, const char *name, Stringpool::Key name_key,
const char *version, bool def, const char *version, Stringpool::Key version_key,
const elfcpp::Sym<size, big_endian>& sym); bool def, const elfcpp::Sym<size, big_endian>& sym);
// Resolve symbols. // Resolve symbols.
template<int size, bool big_endian> template<int size, bool big_endian>
@ -783,7 +783,7 @@ class Symbol_table
// The type of the symbol hash table. // The type of the symbol hash table.
typedef std::pair<const char*, const char*> Symbol_table_key; typedef std::pair<Stringpool::Key, Stringpool::Key> Symbol_table_key;
struct Symbol_table_hash struct Symbol_table_hash
{ {