Another snapshot of the current state of the sources. Gets to the

point of symbol resolution and can now issue a multiple definition
error.  Also added target selection infrastructure.
This commit is contained in:
Ian Lance Taylor 2006-08-18 22:29:20 +00:00
parent 476308bf9b
commit 14bfc3f555
20 changed files with 1674 additions and 110 deletions

View File

@ -396,6 +396,25 @@ enum STT
STT_HIPROC = 15
};
inline STB
elf_st_bind(unsigned char info)
{
return static_cast<STB>(info >> 4);
}
inline STT
elf_st_type(unsigned char info)
{
return static_cast<STT>(info & 0xf);
}
inline unsigned char
elf_st_info(STB bind, STT type)
{
return ((static_cast<unsigned char>(bind) << 4)
+ (static_cast<unsigned char>(type) & 0xf));
}
// Symbol visibility from Sym st_other field.
enum STV
@ -406,6 +425,18 @@ enum STV
STV_PROTECTED = 3
};
inline STV
elf_st_visibility(unsigned char other)
{
return static_cast<STV>(other & 0x3);
}
inline unsigned char
elf_st_nonvis(unsigned char other)
{
return static_cast<STV>(other >> 2);
}
} // End namespace elfcpp.
// Include internal details after defining the types.
@ -576,16 +607,32 @@ class Sym
typename Elf_types<size>::Elf_WXword
get_st_size() const
{ return internal::convert_wxword<big_endian>(this->p_->st_size); }
{ return internal::convert_wxword<size, big_endian>(this->p_->st_size); }
unsigned char
get_st_info() const
{ return this->p_->st_info; }
STB
get_st_bind() const
{ return elf_st_bind(this->get_st_info()); }
STT
get_st_type() const
{ return elf_st_type(this->get_st_info()); }
unsigned char
get_st_other() const
{ return this->p_->st_other; }
STV
get_st_visibility() const
{ return elf_st_visibility(this->get_st_other()); }
unsigned char
get_st_nonvis() const
{ return elf_st_nonvis(this->get_st_other()); }
Elf_Half
get_st_shndx() const
{ return internal::convert_half<big_endian>(this->p_->st_shndx); }

View File

@ -17,7 +17,7 @@ INCLUDES = -D_GNU_SOURCE \
noinst_PROGRAMS = ld-new
CFILES = \
CCFILES = \
dirsearch.cc \
fileread.cc \
gold.cc \
@ -25,6 +25,10 @@ CFILES = \
object.cc \
options.cc \
readsyms.cc \
resolve.cc \
symtab.cc \
stringpool.cc \
target-select.cc \
workqueue.cc
HFILES = \
@ -35,20 +39,25 @@ HFILES = \
object.h \
options.h \
readsyms.h \
stringpool.h \
symtab.h \
target.h \
targetsize.h \
target-select.h \
workqueue.h
TARGETFILES = \
i386.cc
OFILES = gold.o options.o
POTFILES= $(CFILES) $(HFILES)
POTFILES= $(CCFILES) $(HFILES) $(TARGETFILES)
po/POTFILES.in: @MAINT@ Makefile
for f in $(POTFILES); do echo $$f; done | LC_COLLATE= sort > tmp \
&& mv tmp $(srcdir)/po/POTFILES.in
ld_new_SOURCES = $(CFILES) $(HFILES)
ld_new_SOURCES = $(CCFILES) $(HFILES) $(TARGETFILES)
ld_new_DEPENDENCIES = $(LIBINTL_DEP)
ld_new_LDADD = $(LIBINTL)

View File

@ -67,9 +67,12 @@ CONFIG_CLEAN_FILES = po/Makefile.in
PROGRAMS = $(noinst_PROGRAMS)
am__objects_1 = dirsearch.$(OBJEXT) fileread.$(OBJEXT) gold.$(OBJEXT) \
gold-threads.$(OBJEXT) object.$(OBJEXT) options.$(OBJEXT) \
readsyms.$(OBJEXT) workqueue.$(OBJEXT)
readsyms.$(OBJEXT) resolve.$(OBJEXT) symtab.$(OBJEXT) \
stringpool.$(OBJEXT) target-select.$(OBJEXT) \
workqueue.$(OBJEXT)
am__objects_2 =
am_ld_new_OBJECTS = $(am__objects_1) $(am__objects_2)
am__objects_3 = i386.$(OBJEXT)
am_ld_new_OBJECTS = $(am__objects_1) $(am__objects_2) $(am__objects_3)
ld_new_OBJECTS = $(am_ld_new_OBJECTS)
am__DEPENDENCIES_1 =
DEFAULT_INCLUDES = -I. -I$(srcdir) -I.
@ -225,7 +228,7 @@ INCLUDES = -D_GNU_SOURCE \
-DLOCALEDIR="\"$(datadir)/locale\"" \
@INCINTL@
CFILES = \
CCFILES = \
dirsearch.cc \
fileread.cc \
gold.cc \
@ -233,6 +236,10 @@ CFILES = \
object.cc \
options.cc \
readsyms.cc \
resolve.cc \
symtab.cc \
stringpool.cc \
target-select.cc \
workqueue.cc
HFILES = \
@ -243,14 +250,19 @@ HFILES = \
object.h \
options.h \
readsyms.h \
stringpool.h \
symtab.h \
target.h \
targetsize.h \
target-select.h \
workqueue.h
TARGETFILES = \
i386.cc
OFILES = gold.o options.o
POTFILES = $(CFILES) $(HFILES)
ld_new_SOURCES = $(CFILES) $(HFILES)
POTFILES = $(CCFILES) $(HFILES) $(TARGETFILES)
ld_new_SOURCES = $(CCFILES) $(HFILES) $(TARGETFILES)
ld_new_DEPENDENCIES = $(LIBINTL_DEP)
ld_new_LDADD = $(LIBINTL)
all: config.h
@ -327,9 +339,14 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fileread.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gold-threads.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gold.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/i386.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/object.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/readsyms.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resolve.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stringpool.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/symtab.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/target-select.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/workqueue.Po@am__quote@
.cc.o:

View File

@ -11,6 +11,7 @@
#include "workqueue.h"
#include "dirsearch.h"
#include "readsyms.h"
#include "symtab.h"
namespace gold
{
@ -65,7 +66,7 @@ void
queue_initial_tasks(const General_options& options,
const Dirsearch& search_path,
const Command_line::Input_argument_list& inputs,
Workqueue* workqueue)
Workqueue* workqueue, Symbol_table* symtab)
{
if (inputs.empty())
gold_fatal(_("no input files"), false);
@ -81,8 +82,8 @@ queue_initial_tasks(const General_options& options,
{
Task_token* next_blocker = new Task_token();
next_blocker->add_blocker();
workqueue->queue(new Read_symbols(options, search_path, *p, this_blocker,
next_blocker));
workqueue->queue(new Read_symbols(options, symtab, search_path,
*p, this_blocker, next_blocker));
this_blocker = next_blocker;
}
@ -113,6 +114,7 @@ main(int argc, char** argv)
gold::Workqueue workqueue(command_line.options());
// The symbol table.
Symbol_table symtab;
// Get the search path from the -L options.
Dirsearch search_path;
@ -120,7 +122,7 @@ main(int argc, char** argv)
// Queue up the first set of tasks.
queue_initial_tasks(command_line.options(), search_path,
command_line.inputs(), &workqueue);
command_line.inputs(), &workqueue, &symtab);
// Run the main task processing loop.
workqueue.process();

47
gold/i386.cc Normal file
View File

@ -0,0 +1,47 @@
// i386.cc -- i386 target support for gold.
#include "gold.h"
#include "elfcpp.h"
#include "target.h"
#include "target-select.h"
namespace
{
using namespace gold;
// The i386 target class.
class Target_i386 : public Sized_target<32, false>
{
public:
Target_i386()
: Sized_target<32, false>(false, false)
{ }
};
// The selector for i386 object files.
class Target_selector_i386 : public Target_selector
{
public:
Target_selector_i386()
: Target_selector(elfcpp::EM_386, 32, false)
{ }
Target*
recognize(int machine, int osabi, int abiversion) const;
};
// Recognize an i386 object file when we already know that the machine
// number is EM_386.
Target*
Target_selector_i386::recognize(int, int, int) const
{
return new Target_i386();
}
Target_selector_i386 target_selector_i386;
} // End anonymous namespace.

View File

@ -7,6 +7,7 @@
#include <cassert>
#include "object.h"
#include "target-select.h"
namespace gold
{
@ -40,16 +41,16 @@ Sized_object<size, big_endian>::Sized_object(
Input_file* input_file,
off_t offset,
const elfcpp::Ehdr<size, big_endian>& ehdr)
: Object(name, input_file, offset),
: Object(name, input_file, false, offset),
osabi_(ehdr.get_e_ident()[elfcpp::EI_OSABI]),
abiversion_(ehdr.get_e_ident()[elfcpp::EI_ABIVERSION]),
machine_(ehdr.get_e_machine()),
flags_(ehdr.get_e_flags()),
target_(NULL),
shoff_(ehdr.get_e_shoff()),
shnum_(0),
shstrndx_(0),
symtab_shnum_(0)
symtab_shnum_(0),
symbols_(NULL)
{
if (ehdr.get_e_ehsize() != elfcpp::Elf_sizes<size>::ehdr_size)
{
@ -80,8 +81,15 @@ void
Sized_object<size, big_endian>::setup(
const elfcpp::Ehdr<size, big_endian>& ehdr)
{
// this->target_ = select_target(this->machine_, size, big_endian,
// this->osabi_, this->abiversion_);
Target* target = select_target(this->machine_, size, big_endian,
this->osabi_, this->abiversion_);
if (target == NULL)
{
fprintf(stderr, _("%s: %s: unsupported ELF machine number %d\n"),
program_name, this->name().c_str(), this->machine_);
gold_exit(false);
}
this->set_target(target);
unsigned int shnum = ehdr.get_e_shnum();
unsigned int shstrndx = ehdr.get_e_shstrndx();
if ((shnum == 0 || shstrndx == elfcpp::SHN_XINDEX)
@ -143,9 +151,14 @@ Sized_object<size, big_endian>::do_read_symbols()
elfcpp::Shdr<size, big_endian> symtabshdr(psymtabshdr);
assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
// We only need the external symbols.
int sym_size = elfcpp::Elf_sizes<size>::sym_size;
off_t locsize = symtabshdr.get_sh_info() * sym_size;
off_t extoff = symtabshdr.get_sh_offset() + locsize;
off_t extsize = symtabshdr.get_sh_size() - locsize;
// Read the symbol table.
File_view* fvsymtab = this->get_lasting_view(symtabshdr.get_sh_offset(),
symtabshdr.get_sh_size());
File_view* fvsymtab = this->get_lasting_view(extoff, extsize);
// Read the section header for the symbol names.
unsigned int strtab_shnum = symtabshdr.get_sh_link();
@ -173,7 +186,7 @@ Sized_object<size, big_endian>::do_read_symbols()
Read_symbols_data ret;
ret.symbols = fvsymtab;
ret.symbols_size = symtabshdr.get_sh_size();
ret.symbols_size = extsize;
ret.symbol_names = fvstrtab;
ret.symbol_names_size = strtabshdr.get_sh_size();
@ -184,7 +197,8 @@ Sized_object<size, big_endian>::do_read_symbols()
template<int size, bool big_endian>
void
Sized_object<size, big_endian>::do_add_symbols(Read_symbols_data sd)
Sized_object<size, big_endian>::do_add_symbols(Symbol_table* symtab,
Read_symbols_data sd)
{
if (sd.symbols == NULL)
{
@ -192,25 +206,24 @@ Sized_object<size, big_endian>::do_add_symbols(Read_symbols_data sd)
return;
}
int sym_size = elfcpp::Elf_sizes<size>::sym_size;
const unsigned char* symstart = sd.symbols->data();
const unsigned char* symend = symstart + sd.symbols_size;
for (const unsigned char* p = symstart; p < symend; p += sym_size)
unsigned int sym_size = elfcpp::Elf_sizes<size>::sym_size;
size_t symcount = sd.symbols_size / sym_size;
if (symcount * sym_size != sd.symbols_size)
{
elfcpp::Sym<size, big_endian> sym(p);
unsigned int nameoff = sym.get_st_name();
if (nameoff >= sd.symbol_names_size)
{
fprintf(stderr,
_("%s: %s: invalid symbol name offset %u for symbol %d\n"),
program_name, this->name().c_str(), nameoff,
(p - symstart) / sym_size);
gold_exit(false);
}
const unsigned char* name = sd.symbol_names->data() + nameoff;
printf("%s\n", name);
fprintf(stderr,
_("%s: %s: size of symbols is not multiple of symbol size\n"),
program_name, this->name().c_str());
gold_exit(false);
}
this->symbols_ = new Symbol*[symcount];
const elfcpp::Sym<size, big_endian>* syms =
reinterpret_cast<const elfcpp::Sym<size, big_endian>*>(sd.symbols->data());
const char* sym_names =
reinterpret_cast<const char*>(sd.symbol_names->data());
symtab->add_from_object(this, syms, symcount, sym_names,
sd.symbol_names_size, this->symbols_);
}
} // End namespace gold.

View File

@ -3,10 +3,12 @@
#ifndef GOLD_OBJECT_H
#define GOLD_OBJECT_H
#include <cassert>
#include "elfcpp.h"
#include "targetsize.h"
#include "target.h"
#include "fileread.h"
#include "target.h"
#include "symtab.h"
namespace gold
{
@ -26,8 +28,9 @@ struct Read_symbols_data
};
// Object is an interface which represents either a 32-bit or a 64-bit
// object file. The actual instantiations are Sized_object<32> and
// Sized_object<64>
// input object. This can be a regular object file (ET_REL) or a
// shared object (ET_DYN). The actual instantiations are
// Sized_object<32> and Sized_object<64>
class Object
{
@ -36,17 +39,25 @@ class Object
// (e.g., libfoo.a(bar.o) if this is in an archive. INPUT_FILE is
// used to read the file. OFFSET is the offset within the input
// file--0 for a .o or .so file, something else for a .a file.
Object(const std::string& name, Input_file* input_file, off_t offset = 0)
: name_(name), input_file_(input_file), offset_(offset)
Object(const std::string& name, Input_file* input_file, bool is_dynamic,
off_t offset = 0)
: name_(name), input_file_(input_file), offset_(offset),
is_dynamic_(is_dynamic), target_(NULL)
{ }
virtual ~Object()
{ }
// Return the name of the object as we would report it to the tuser.
const std::string&
name() const
{ return this->name_; }
// Return whether this is a dynamic object.
bool
is_dynamic() const
{ return this->is_dynamic_; }
// Read the symbol and relocation information.
Read_symbols_data
read_symbols()
@ -54,8 +65,20 @@ class Object
// Add symbol information to the global symbol table.
void
add_symbols(Read_symbols_data rd)
{ this->do_add_symbols(rd); }
add_symbols(Symbol_table* symtab, Read_symbols_data rd)
{ this->do_add_symbols(symtab, rd); }
// Return the target structure associated with this object.
Target*
target()
{ return this->target_; }
// Return the sized target structure associated with this object.
// This is like the target method but it returns a pointer of
// appropriate checked type.
template<int size, bool big_endian>
Sized_target<size, big_endian>*
sized_target();
protected:
// Read the symbols--implemented by child class.
@ -65,7 +88,7 @@ class Object
// Add symbol information to the global symbol table--implemented by
// child class.
virtual void
do_add_symbols(Read_symbols_data) = 0;
do_add_symbols(Symbol_table*, Read_symbols_data) = 0;
// Get the file.
Input_file*
@ -81,6 +104,11 @@ class Object
const unsigned char*
get_view(off_t start, off_t size);
// Set the target.
void
set_target(Target* target)
{ this->target_ = target; }
// Read data from the underlying file.
void
read(off_t start, off_t size, void* p);
@ -101,9 +129,25 @@ class Object
// Offset within the file--0 for an object file, non-0 for an
// archive.
off_t offset_;
// Whether this is a dynamic object.
bool is_dynamic_;
// Target functions--may be NULL if the target is not known.
Target* target_;
};
// The functions of Object which are size specific.
// Implement sized_target inline for efficiency. This approach breaks
// static type checking, but is made safe using asserts.
template<int size, bool big_endian>
inline Sized_target<size, big_endian>*
Object::sized_target()
{
assert(this->target_->get_size() == size);
assert(this->target_->is_big_endian() ? big_endian : !big_endian);
return static_cast<Sized_target<size, big_endian>*>(this->target_);
}
// A regular object file. This is size and endian specific.
template<int size, bool big_endian>
class Sized_object : public Object
@ -121,7 +165,11 @@ class Sized_object : public Object
do_read_symbols();
void
do_add_symbols(Read_symbols_data);
do_add_symbols(Symbol_table*, Read_symbols_data);
Sized_target<size, big_endian>*
sized_target()
{ return this->Object::sized_target<size, big_endian>(); }
private:
// This object may not be copied.
@ -136,8 +184,6 @@ class Sized_object : public Object
elfcpp::Elf_Half machine_;
// ELF file header e_flags field.
unsigned int flags_;
// Target functions--may be NULL.
Target* target_;
// File offset of section header table.
off_t shoff_;
// Number of input sections.
@ -146,6 +192,8 @@ class Sized_object : public Object
unsigned int shstrndx_;
// Index of SHT_SYMTAB section.
unsigned int symtab_shnum_;
// The entries in the symbol table for the external symbols.
Symbol** symbols_;
};
// Return an Object appropriate for the input file. P is BYTES long,

View File

@ -6,14 +6,21 @@ gold.cc
gold.h
gold-threads.cc
gold-threads.h
i386.cc
object.cc
object.h
options.cc
options.h
readsyms.cc
readsyms.h
resolve.cc
stringpool.cc
stringpool.h
symtab.cc
symtab.h
target.h
target-select.cc
target-select.h
targetsize.h
workqueue.cc
workqueue.h

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2006-08-04 14:23-0700\n"
"POT-Creation-Date: 2006-08-18 15:26-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -16,7 +16,7 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: dirsearch.cc:50
#: dirsearch.cc:51
#, c-format
msgid "can not read directory %s"
msgstr ""
@ -51,7 +51,7 @@ msgstr ""
msgid "%s: cannot open %s: %s"
msgstr ""
#: gold.cc:71
#: gold.cc:72
msgid "no input files"
msgstr ""
@ -99,68 +99,73 @@ msgstr ""
msgid "pthread_cond_signal failed"
msgstr ""
#: object.cc:56
#: object.cc:57
#, c-format
msgid "%s: %s: bad e_ehsize field (%d != %d)\n"
msgstr ""
#: object.cc:63
#: object.cc:64
#, c-format
msgid "%s: %s: bad e_shentsize field (%d != %d)\n"
msgstr ""
#: object.cc:154
#: object.cc:88
#, c-format
msgid "%s: %s: unsupported ELF machine number %d\n"
msgstr ""
#: object.cc:167
#, c-format
msgid "%s: %s: invalid symbol table name index: %u\n"
msgstr ""
#: object.cc:164
#: object.cc:177
#, c-format
msgid "%s: %s: symbol table name section has wrong type: %u\n"
msgstr ""
#: object.cc:206
#: object.cc:214
#, c-format
msgid "%s: %s: invalid symbol name offset %u for symbol %d\n"
msgid "%s: %s: size of symbols is not multiple of symbol size\n"
msgstr ""
#. elfcpp::ET_DYN
#: object.cc:249
#: object.cc:262
#, c-format
msgid "%s: %s: dynamic objects are not yet supported\n"
msgstr ""
#: object.cc:273 object.cc:326 object.cc:347
#: object.cc:286 object.cc:339 object.cc:360
#, c-format
msgid "%s: %s: ELF file too short\n"
msgstr ""
#: object.cc:282
#: object.cc:295
#, c-format
msgid "%s: %s: invalid ELF version 0\n"
msgstr ""
#: object.cc:285
#: object.cc:298
#, c-format
msgid "%s: %s: unsupported ELF version %d\n"
msgstr ""
#: object.cc:293
#: object.cc:306
#, c-format
msgid "%s: %s: invalid ELF class 0\n"
msgstr ""
#: object.cc:300
#: object.cc:313
#, c-format
msgid "%s: %s: unsupported ELF class %d\n"
msgstr ""
#: object.cc:308
#: object.cc:321
#, c-format
msgid "%s: %s: invalid ELF data encoding\n"
msgstr ""
#: object.cc:315
#: object.cc:328
#, c-format
msgid "%s: %s: unsupported ELF data encoding %d\n"
msgstr ""
@ -214,3 +219,23 @@ msgstr ""
#, c-format
msgid "%s: -%c: %s\n"
msgstr ""
#: resolve.cc:103
#, c-format
msgid "%s: %s: invalid STB_LOCAL symbol %s in external symbols\n"
msgstr ""
#: resolve.cc:109
#, c-format
msgid "%s: %s: unsupported symbol binding %d for symbol %s\n"
msgstr ""
#: symtab.cc:262
#, c-format
msgid "%s: %s: mixing 32-bit and 64-bit ELF objects\n"
msgstr ""
#: symtab.cc:275
#, c-format
msgid "%s: %s: bad symbol name offset %u at %lu\n"
msgstr ""

View File

@ -73,7 +73,8 @@ Read_symbols::run(Workqueue* workqueue)
p, bytes);
Read_symbols_data sd = obj->read_symbols();
workqueue->queue(new Add_symbols(obj, sd, this->this_blocker_,
workqueue->queue(new Add_symbols(this->symtab_, obj, sd,
this->this_blocker_,
this->next_blocker_));
// Opening the file locked it, so now we need to unlock it.
@ -117,7 +118,7 @@ Add_symbols::locks(Workqueue* workqueue)
void
Add_symbols::run(Workqueue*)
{
this->object_->add_symbols(this->sd_);
this->object_->add_symbols(this->symtab_, this->sd_);
}
} // End namespace gold.

View File

@ -26,10 +26,10 @@ class Read_symbols : public Task
// associated Add_symbols task from running before the previous one
// has completed; it will be NULL for the first task. NEXT_BLOCKER
// is used to block the next input file from adding symbols.
Read_symbols(const General_options& options, const Dirsearch& dirpath,
const Input_argument& input, Task_token* this_blocker,
Task_token* next_blocker)
: options_(options), dirpath_(dirpath), input_(input),
Read_symbols(const General_options& options, Symbol_table* symtab,
const Dirsearch& dirpath, const Input_argument& input,
Task_token* this_blocker, Task_token* next_blocker)
: options_(options), symtab_(symtab), dirpath_(dirpath), input_(input),
this_blocker_(this_blocker), next_blocker_(next_blocker)
{ }
@ -48,6 +48,7 @@ class Read_symbols : public Task
private:
const General_options& options_;
Symbol_table* symtab_;
const Dirsearch& dirpath_;
const Input_argument& input_;
Task_token* this_blocker_;
@ -64,9 +65,9 @@ class Add_symbols : public Task
// THIS_BLOCKER is used to prevent this task from running before the
// one for the previous input file. NEXT_BLOCKER is used to prevent
// the next task from running.
Add_symbols(Object* object, Read_symbols_data sd, Task_token* this_blocker,
Task_token* next_blocker)
: object_(object), sd_(sd), this_blocker_(this_blocker),
Add_symbols(Symbol_table* symtab, Object* object, Read_symbols_data sd,
Task_token* this_blocker, Task_token* next_blocker)
: symtab_(symtab), object_(object), sd_(sd), this_blocker_(this_blocker),
next_blocker_(next_blocker)
{ }
@ -84,6 +85,7 @@ class Add_symbols : public Task
run(Workqueue*);
private:
Symbol_table* symtab_;
Object* object_;
Read_symbols_data sd_;
Task_token* this_blocker_;

349
gold/resolve.cc Normal file
View File

@ -0,0 +1,349 @@
// resolve.cc -- symbol resolution for gold
#include "gold.h"
#include "elfcpp.h"
#include "target.h"
#include "object.h"
#include "symtab.h"
namespace gold
{
// Resolve a symbol. This is called the second and subsequent times
// we see a symbol. TO is the pre-existing symbol. SYM is the new
// symbol, seen in OBJECT.
template<int size, bool big_endian>
void
Symbol_table::resolve(Symbol* to,
const elfcpp::Sym<size, big_endian>& sym,
Object* object)
{
if (object->target()->has_resolve())
{
object->sized_target<size, big_endian>()->resolve(to, sym, object);
return;
}
// Build a little code for each symbol.
// Bit 0: 0 for global, 1 for weak.
// Bit 1: 0 for regular object, 1 for shared object
// Bits 2-3: 0 for normal, 1 for undefined, 2 for common
// This gives us values from 0 to 11:
enum
{
DEF = 0,
WEAK_DEF = 1,
DYN_DEF = 2,
DYN_WEAK_DEF = 3,
UNDEF = 4,
WEAK_UNDEF = 5,
DYN_UNDEF = 6,
DYN_WEAK_UNDEF = 7,
COMMON = 8,
WEAK_COMMON = 9,
DYN_COMMON = 10,
DYN_WEAK_COMMON = 11
};
int tobits;
switch (to->binding())
{
case elfcpp::STB_GLOBAL:
tobits = 0;
break;
case elfcpp::STB_WEAK:
tobits = 1;
break;
case elfcpp::STB_LOCAL:
// We should only see externally visible symbols in the symbol
// table.
abort();
default:
// Any target which wants to handle STB_LOOS, etc., needs to
// define a resolve method.
abort();
}
if (to->object() != NULL && to->object()->is_dynamic())
tobits |= (1 << 1);
switch (to->shnum())
{
case elfcpp::SHN_UNDEF:
tobits |= (1 << 2);
break;
case elfcpp::SHN_COMMON:
tobits |= (2 << 2);
break;
default:
break;
}
int frombits;
switch (sym.get_st_bind())
{
case elfcpp::STB_GLOBAL:
frombits = 0;
break;
case elfcpp::STB_WEAK:
frombits = 1;
break;
case elfcpp::STB_LOCAL:
fprintf(stderr,
_("%s: %s: invalid STB_LOCAL symbol %s in external symbols\n"),
program_name, object->name().c_str(), to->name());
gold_exit(false);
default:
fprintf(stderr,
_("%s: %s: unsupported symbol binding %d for symbol %s\n"),
program_name, object->name().c_str(),
static_cast<int>(sym.get_st_bind()), to->name());
gold_exit(false);
}
if (object->is_dynamic())
frombits |= (1 << 1);
switch (sym.get_st_shndx())
{
case elfcpp::SHN_UNDEF:
frombits |= (1 << 2);
break;
case elfcpp::SHN_COMMON:
frombits |= (2 << 2);
break;
default:
break;
}
// We use a giant switch table for symbol resolution. This code is
// unwieldy, but: 1) it is efficient; 2) we definitely handle all
// cases; 3) it is easy to change the handling of a particular case.
// The alternative would be a series of conditionals, but it is easy
// to get the ordering wrong. This could also be done as a table,
// but that is no easier to understand than this large switch
// statement.
switch (tobits * 16 + frombits)
{
case DEF * 16 + DEF:
// Two definitions of the same symbol.
fprintf(stderr, "%s: %s: multiple definition of %s\n",
program_name, object->name().c_str(), to->name());
// FIXME: Report locations. Record that we have seen an error.
return;
case WEAK_DEF * 16 + DEF:
// In the original SVR4 linker, a weak definition followed by a
// regular definition was treated as a multiple definition
// error. In the Solaris linker and the GNU linker, a weak
// definition followed by a regular definition causes the
// regular definition to be ignored. We are currently
// compatible with the GNU linker. In the future we should add
// a target specific option to change this. FIXME.
return;
case DYN_DEF * 16 + DEF:
case DYN_WEAK_DEF * 16 + DEF:
case UNDEF * 16 + DEF:
case WEAK_UNDEF * 16 + DEF:
case DYN_UNDEF * 16 + DEF:
case DYN_WEAK_UNDEF * 16 + DEF:
case COMMON * 16 + DEF:
case WEAK_COMMON * 16 + DEF:
case DYN_COMMON * 16 + DEF:
case DYN_WEAK_COMMON * 16 + DEF:
case DEF * 16 + WEAK_DEF:
case WEAK_DEF * 16 + WEAK_DEF:
case DYN_DEF * 16 + WEAK_DEF:
case DYN_WEAK_DEF * 16 + WEAK_DEF:
case UNDEF * 16 + WEAK_DEF:
case WEAK_UNDEF * 16 + WEAK_DEF:
case DYN_UNDEF * 16 + WEAK_DEF:
case DYN_WEAK_UNDEF * 16 + WEAK_DEF:
case COMMON * 16 + WEAK_DEF:
case WEAK_COMMON * 16 + WEAK_DEF:
case DYN_COMMON * 16 + WEAK_DEF:
case DYN_WEAK_COMMON * 16 + WEAK_DEF:
case DEF * 16 + DYN_DEF:
case WEAK_DEF * 16 + DYN_DEF:
case DYN_DEF * 16 + DYN_DEF:
case DYN_WEAK_DEF * 16 + DYN_DEF:
case UNDEF * 16 + DYN_DEF:
case WEAK_UNDEF * 16 + DYN_DEF:
case DYN_UNDEF * 16 + DYN_DEF:
case DYN_WEAK_UNDEF * 16 + DYN_DEF:
case COMMON * 16 + DYN_DEF:
case WEAK_COMMON * 16 + DYN_DEF:
case DYN_COMMON * 16 + DYN_DEF:
case DYN_WEAK_COMMON * 16 + DYN_DEF:
case DEF * 16 + DYN_WEAK_DEF:
case WEAK_DEF * 16 + DYN_WEAK_DEF:
case DYN_DEF * 16 + DYN_WEAK_DEF:
case DYN_WEAK_DEF * 16 + DYN_WEAK_DEF:
case UNDEF * 16 + DYN_WEAK_DEF:
case WEAK_UNDEF * 16 + DYN_WEAK_DEF:
case DYN_UNDEF * 16 + DYN_WEAK_DEF:
case DYN_WEAK_UNDEF * 16 + DYN_WEAK_DEF:
case COMMON * 16 + DYN_WEAK_DEF:
case WEAK_COMMON * 16 + DYN_WEAK_DEF:
case DYN_COMMON * 16 + DYN_WEAK_DEF:
case DYN_WEAK_COMMON * 16 + DYN_WEAK_DEF:
case DEF * 16 + UNDEF:
case WEAK_DEF * 16 + UNDEF:
case DYN_DEF * 16 + UNDEF:
case DYN_WEAK_DEF * 16 + UNDEF:
case UNDEF * 16 + UNDEF:
case WEAK_UNDEF * 16 + UNDEF:
case DYN_UNDEF * 16 + UNDEF:
case DYN_WEAK_UNDEF * 16 + UNDEF:
case COMMON * 16 + UNDEF:
case WEAK_COMMON * 16 + UNDEF:
case DYN_COMMON * 16 + UNDEF:
case DYN_WEAK_COMMON * 16 + UNDEF:
case DEF * 16 + WEAK_UNDEF:
case WEAK_DEF * 16 + WEAK_UNDEF:
case DYN_DEF * 16 + WEAK_UNDEF:
case DYN_WEAK_DEF * 16 + WEAK_UNDEF:
case UNDEF * 16 + WEAK_UNDEF:
case WEAK_UNDEF * 16 + WEAK_UNDEF:
case DYN_UNDEF * 16 + WEAK_UNDEF:
case DYN_WEAK_UNDEF * 16 + WEAK_UNDEF:
case COMMON * 16 + WEAK_UNDEF:
case WEAK_COMMON * 16 + WEAK_UNDEF:
case DYN_COMMON * 16 + WEAK_UNDEF:
case DYN_WEAK_COMMON * 16 + WEAK_UNDEF:
case DEF * 16 + DYN_UNDEF:
case WEAK_DEF * 16 + DYN_UNDEF:
case DYN_DEF * 16 + DYN_UNDEF:
case DYN_WEAK_DEF * 16 + DYN_UNDEF:
case UNDEF * 16 + DYN_UNDEF:
case WEAK_UNDEF * 16 + DYN_UNDEF:
case DYN_UNDEF * 16 + DYN_UNDEF:
case DYN_WEAK_UNDEF * 16 + DYN_UNDEF:
case COMMON * 16 + DYN_UNDEF:
case WEAK_COMMON * 16 + DYN_UNDEF:
case DYN_COMMON * 16 + DYN_UNDEF:
case DYN_WEAK_COMMON * 16 + DYN_UNDEF:
case DEF * 16 + DYN_WEAK_UNDEF:
case WEAK_DEF * 16 + DYN_WEAK_UNDEF:
case DYN_DEF * 16 + DYN_WEAK_UNDEF:
case DYN_WEAK_DEF * 16 + DYN_WEAK_UNDEF:
case UNDEF * 16 + DYN_WEAK_UNDEF:
case WEAK_UNDEF * 16 + DYN_WEAK_UNDEF:
case DYN_UNDEF * 16 + DYN_WEAK_UNDEF:
case DYN_WEAK_UNDEF * 16 + DYN_WEAK_UNDEF:
case COMMON * 16 + DYN_WEAK_UNDEF:
case WEAK_COMMON * 16 + DYN_WEAK_UNDEF:
case DYN_COMMON * 16 + DYN_WEAK_UNDEF:
case DYN_WEAK_COMMON * 16 + DYN_WEAK_UNDEF:
case DEF * 16 + COMMON:
case WEAK_DEF * 16 + COMMON:
case DYN_DEF * 16 + COMMON:
case DYN_WEAK_DEF * 16 + COMMON:
case UNDEF * 16 + COMMON:
case WEAK_UNDEF * 16 + COMMON:
case DYN_UNDEF * 16 + COMMON:
case DYN_WEAK_UNDEF * 16 + COMMON:
case COMMON * 16 + COMMON:
case WEAK_COMMON * 16 + COMMON:
case DYN_COMMON * 16 + COMMON:
case DYN_WEAK_COMMON * 16 + COMMON:
case DEF * 16 + WEAK_COMMON:
case WEAK_DEF * 16 + WEAK_COMMON:
case DYN_DEF * 16 + WEAK_COMMON:
case DYN_WEAK_DEF * 16 + WEAK_COMMON:
case UNDEF * 16 + WEAK_COMMON:
case WEAK_UNDEF * 16 + WEAK_COMMON:
case DYN_UNDEF * 16 + WEAK_COMMON:
case DYN_WEAK_UNDEF * 16 + WEAK_COMMON:
case COMMON * 16 + WEAK_COMMON:
case WEAK_COMMON * 16 + WEAK_COMMON:
case DYN_COMMON * 16 + WEAK_COMMON:
case DYN_WEAK_COMMON * 16 + WEAK_COMMON:
case DEF * 16 + DYN_COMMON:
case WEAK_DEF * 16 + DYN_COMMON:
case DYN_DEF * 16 + DYN_COMMON:
case DYN_WEAK_DEF * 16 + DYN_COMMON:
case UNDEF * 16 + DYN_COMMON:
case WEAK_UNDEF * 16 + DYN_COMMON:
case DYN_UNDEF * 16 + DYN_COMMON:
case DYN_WEAK_UNDEF * 16 + DYN_COMMON:
case COMMON * 16 + DYN_COMMON:
case WEAK_COMMON * 16 + DYN_COMMON:
case DYN_COMMON * 16 + DYN_COMMON:
case DYN_WEAK_COMMON * 16 + DYN_COMMON:
case DEF * 16 + DYN_WEAK_COMMON:
case WEAK_DEF * 16 + DYN_WEAK_COMMON:
case DYN_DEF * 16 + DYN_WEAK_COMMON:
case DYN_WEAK_DEF * 16 + DYN_WEAK_COMMON:
case UNDEF * 16 + DYN_WEAK_COMMON:
case WEAK_UNDEF * 16 + DYN_WEAK_COMMON:
case DYN_UNDEF * 16 + DYN_WEAK_COMMON:
case DYN_WEAK_UNDEF * 16 + DYN_WEAK_COMMON:
case COMMON * 16 + DYN_WEAK_COMMON:
case WEAK_COMMON * 16 + DYN_WEAK_COMMON:
case DYN_COMMON * 16 + DYN_WEAK_COMMON:
case DYN_WEAK_COMMON * 16 + DYN_WEAK_COMMON:
break;
}
}
// Instantiate the templates we need. We could use the configure
// script to restrict this to only the ones needed for implemented
// targets.
template
void
Symbol_table::resolve<32, true>(
Symbol* to,
const elfcpp::Sym<32, true>& sym,
Object* object);
template
void
Symbol_table::resolve<32, false>(
Symbol* to,
const elfcpp::Sym<32, false>& sym,
Object* object);
template
void
Symbol_table::resolve<64, true>(
Symbol* to,
const elfcpp::Sym<64, true>& sym,
Object* object);
template
void
Symbol_table::resolve<64, false>(
Symbol* to,
const elfcpp::Sym<64, false>& sym,
Object* object);
} // End namespace gold.

130
gold/stringpool.cc Normal file
View File

@ -0,0 +1,130 @@
// stringpool.cc -- a string pool for gold
#include "gold.h"
#include <cassert>
#include <cstring>
#include "stringpool.h"
namespace gold
{
Stringpool::Stringpool()
: string_set_(), strings_()
{
}
Stringpool::~Stringpool()
{
for (std::list<stringdata*>::iterator p = this->strings_.begin();
p != this->strings_.end();
++p)
delete[] reinterpret_cast<char*>(*p);
}
// Hash function.
size_t
Stringpool::Stringpool_hash::operator()(const char* s) const
{
// Fowler/Noll/Vo (FNV) hash (type FNV-1a).
if (sizeof(size_t) == 8)
{
size_t result = 14695981039346656037ULL;
while (*s != '\0')
{
result &= (size_t) *s++;
result *= 1099511628211ULL;
}
return result;
}
else
{
size_t result = 2166136261UL;
while (*s != '\0')
{
result ^= (size_t) *s++;
result *= 16777619UL;
}
return result;
}
}
// Add a string to the list of canonical strings. Return a pointer to
// the canonical string.
const char*
Stringpool::add_string(const char* s)
{
const size_t buffer_size = 1000;
size_t len = strlen(s);
size_t alc;
bool front = true;
if (len >= buffer_size)
{
alc = sizeof(stringdata) + len;
front = false;
}
else if (this->strings_.empty())
alc = sizeof(stringdata) + buffer_size;
else
{
stringdata *psd = this->strings_.front();
if (len >= psd->alc - psd->len)
alc = sizeof(stringdata) + buffer_size;
else
{
char* ret = psd->data + psd->len;
memcpy(ret, s, len + 1);
psd->len += len + 1;
return ret;
}
}
stringdata *psd = reinterpret_cast<stringdata*>(new char[alc]);
psd->alc = alc;
memcpy(psd->data, s, len + 1);
psd->len = len + 1;
if (front)
this->strings_.push_front(psd);
else
this->strings_.push_back(psd);
return psd->data;
}
// Add a string to a string pool.
const char*
Stringpool::add(const char* s)
{
// 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
// don't think there is a way to handle this correct with
// unordered_set, so this should be replaced with custom code to do
// what we need, which is to return the empty slot.
String_set_type::const_iterator p = this->string_set_.find(s);
if (p != this->string_set_.end())
return *p;
const char* ret = this->add_string(s);
std::pair<String_set_type::iterator, bool> ins =
this->string_set_.insert(ret);
assert(ins.second);
return ret;
}
// Add a prefix of a string to a string pool.
const char*
Stringpool::add(const char* s, size_t len)
{
// FIXME: This implementation should be rewritten when we rewrite
// the hash table to avoid copying.
std::string st(s, len);
return this->add(st);
}
} // End namespace gold.

70
gold/stringpool.h Normal file
View File

@ -0,0 +1,70 @@
// stringpool.h -- a string pool for gold -*- C++ -*-
#include <string>
#include <list>
// Stringpool
// Manage a pool of unique strings.
#ifndef GOLD_STRINGPOOL_H
#define GOLD_STRINGPOOL_H
namespace gold
{
class Stringpool
{
public:
Stringpool();
~Stringpool();
// Add a string to the pool. This returns a canonical permanent
// pointer to the string.
const char* add(const char*);
const char* add(const std::string& s)
{ return this->add(s.c_str()); }
// Add the prefix of a string to the pool.
const char* add(const char *, size_t);
private:
Stringpool(const Stringpool&);
Stringpool& operator=(const Stringpool&);
struct stringdata
{
// Length of data in buffer.
size_t len;
// Allocated size of buffer.
size_t alc;
// Buffer.
char data[1];
};
const char* add_string(const char*);
struct Stringpool_hash
{
size_t
operator()(const char*) const;
};
struct Stringpool_eq
{
bool
operator()(const char* p1, const char* p2) const
{ return strcmp(p1, p2) == 0; }
};
typedef Unordered_set<const char*, Stringpool_hash, Stringpool_eq,
std::allocator<const char*>,
true> String_set_type;
String_set_type string_set_;
std::list<stringdata*> strings_;
};
} // End namespace gold.
#endif // !defined(GOLD_STRINGPOOL_H)

73
gold/strtab.h Normal file
View File

@ -0,0 +1,73 @@
// strtab.h -- manage an ELF string table for gold -*- C++ -*-
#ifndef GOLD_STRTAB_H
#define GOLD_STRTAB_H
#include <cstring>
#include <string>
namespace gold
{
// This class holds an ELF string table. We keep a reference count
// for each string, which we use to determine which strings are
// actually required at the end. When all operations are done, the
// string table is finalized, which sets the offsets to use for each
// string.
class Strtab
{
public:
Strtab();
~Strtab();
Strtab_ref* add(const char*);
Strtab_ref* add(const std::string& s)
{ return this->add(s.c_str()); }
private:
Strtab(const Strtab&);
Strtab& operator=(const Strtab&);
struct strtab_hash
{
std::size_t
operator()(const char*p);
};
struct strtab_eq
{
bool
operator()(const char* p1, const char* p2)
{ return strcmp(p1, p2) == 0; }
};
Unordered_map<const char*, Strtab_ref*, strtab_hash, strtab_eq,
std::allocator<std::pair<const char* const, Strtab_ref*> >,
true> strings_;
};
// Users of Strtab work with pointers to Strtab_ref structures. These
// are allocated via new and should be deleted if the string is no
// longer needed.
class Strtab_ref
{
public:
~Strtab_ref();
const char*
str() const;
private:
Strtab_ref(const Strtab_ref&);
Strtab_ref& operator=(const Strtab_ref&);
int refs_;
};
} // End namespace gold.
#endif // !defined(GOLD_STRTAB_H)

358
gold/symtab.cc Normal file
View File

@ -0,0 +1,358 @@
// symtab.cc -- the gold symbol table
#include "gold.h"
#include <cassert>
#include <stdint.h>
#include <string>
#include <utility>
#include "object.h"
#include "symtab.h"
namespace gold
{
// Class Symbol.
Symbol::~Symbol()
{
}
// Initialize the fields in the base class Symbol.
template<int size, bool big_endian>
void
Symbol::init_base(const char* name, const char* version, Object* object,
const elfcpp::Sym<size, big_endian>& sym)
{
this->name_ = name;
this->version_ = version;
this->object_ = object;
this->shnum_ = sym.get_st_shndx(); // FIXME: Handle SHN_XINDEX.
this->type_ = sym.get_st_type();
this->binding_ = sym.get_st_bind();
this->visibility_ = sym.get_st_visibility();
this->other_ = sym.get_st_nonvis();
this->special_ = false;
this->def_ = false;
this->forwarder_ = false;
}
// Initialize the fields in Sized_symbol.
template<int size>
template<bool big_endian>
void
Sized_symbol<size>::init(const char* name, const char* version, Object* object,
const elfcpp::Sym<size, big_endian>& sym)
{
this->init_base(name, version, object, sym);
this->value_ = sym.get_st_value();
this->size_ = sym.get_st_size();
}
// Class Symbol_table.
Symbol_table::Symbol_table()
: size_(0), table_(), namepool_(), forwarders_()
{
}
Symbol_table::~Symbol_table()
{
}
// The hash function. The key is always canonicalized, so we use a
// simple combination of the pointers.
size_t
Symbol_table::Symbol_table_hash::operator()(const Symbol_table_key& key) const
{
return (reinterpret_cast<size_t>(key.first)
^ reinterpret_cast<size_t>(key.second));
}
// The symbol table key equality function. This is only called with
// canonicalized name and version strings, so we can use pointer
// comparison.
bool
Symbol_table::Symbol_table_eq::operator()(const Symbol_table_key& k1,
const Symbol_table_key& k2) const
{
return k1.first == k2.first && k1.second == k2.second;
}
// Make TO a symbol which forwards to FROM.
void
Symbol_table::make_forwarder(Symbol* from, Symbol* to)
{
assert(!from->is_forwarder() && !to->is_forwarder());
this->forwarders_[from] = to;
from->set_forwarder();
}
Symbol*
Symbol_table::resolve_forwards(Symbol* from) const
{
assert(from->is_forwarder());
Unordered_map<Symbol*, Symbol*>::const_iterator p =
this->forwarders_.find(from);
assert(p != this->forwarders_.end());
return p->second;
}
// Resolve a Symbol with another Symbol. This is only used in the
// unusual case where there are references to both an unversioned
// symbol and a symbol with a version, and we then discover that that
// version is the default version.
void
Symbol_table::resolve(Symbol*, const Symbol*)
{
}
// Add one symbol from OBJECT to the symbol table. NAME is symbol
// name and VERSION is the version; both are canonicalized. DEF is
// whether this is the default version.
// If DEF is true, then this is the definition of a default version of
// a symbol. That means that any lookup of NAME/NULL and any lookup
// of NAME/VERSION should always return the same symbol. This is
// obvious for references, but in particular we want to do this for
// definitions: overriding NAME/NULL should also override
// NAME/VERSION. If we don't do that, it would be very hard to
// override functions in a shared library which uses versioning.
// We implement this by simply making both entries in the hash table
// point to the same Symbol structure. That is easy enough if this is
// the first time we see NAME/NULL or NAME/VERSION, but it is possible
// that we have seen both already, in which case they will both have
// independent entries in the symbol table. We can't simply change
// the symbol table entry, because we have pointers to the entries
// attached to the object files. So we mark the entry attached to the
// object file as a forwarder, and record it in the forwarders_ map.
// Note that entries in the hash table will never be marked as
// forwarders.
template<int size, bool big_endian>
Symbol*
Symbol_table::add_from_object(Sized_object<size, big_endian>* object,
const char *name,
const char *version, bool def,
const elfcpp::Sym<size, big_endian>& sym)
{
Symbol* const snull = NULL;
std::pair<typename Symbol_table_type::iterator, bool> ins =
this->table_.insert(std::make_pair(std::make_pair(name, version), snull));
std::pair<typename Symbol_table_type::iterator, bool> insdef =
std::make_pair(this->table_.end(), false);
if (def)
{
const char* const vnull = NULL;
insdef = this->table_.insert(std::make_pair(std::make_pair(name, vnull),
snull));
}
// ins.first: an iterator, which is a pointer to a pair.
// ins.first->first: the key (a pair of name and version).
// ins.first->second: the value (Symbol*).
// ins.second: true if new entry was inserted, false if not.
Symbol* ret;
if (!ins.second)
{
// We already have an entry for NAME/VERSION.
ret = ins.first->second;
assert(ret != NULL);
Symbol_table::resolve(ret, sym, object);
if (def)
{
if (insdef.second)
{
// This is the first time we have seen NAME/NULL. Make
// NAME/NULL point to NAME/VERSION.
insdef.first->second = ret;
}
else
{
// This is the unfortunate case where we already have
// entries for both NAME/VERSION and NAME/NULL.
Symbol_table::resolve(ret, insdef.first->second);
this->make_forwarder(insdef.first->second, ret);
insdef.first->second = ret;
}
}
}
else
{
// This is the first time we have seen NAME/VERSION.
assert(ins.first->second == NULL);
if (def && !insdef.second)
{
// We already have an entry for NAME/NULL. Make
// NAME/VERSION point to it.
ret = insdef.first->second;
Symbol_table::resolve(ret, sym, object);
ins.first->second = ret;
}
else
{
Sized_symbol<size>* rs;
Sized_target<size, big_endian>* target = object->sized_target();
if (target->has_make_symbol())
{
rs = target->make_symbol();
if (rs == NULL)
{
// This means that we don't want a symbol table
// entry after all.
if (!def)
this->table_.erase(ins.first);
else
{
this->table_.erase(insdef.first);
// Inserting insdef invalidated ins.
this->table_.erase(std::make_pair(name, version));
}
return NULL;
}
}
else
rs = new Sized_symbol<size>();
rs->init(name, version, object, sym);
ret = rs;
ins.first->second = ret;
if (def)
{
// This is the first time we have seen NAME/NULL. Point
// it at the new entry for NAME/VERSION.
assert(insdef.second);
insdef.first->second = ret;
}
}
}
return ret;
}
// Add all the symbols in an object to the hash table.
template<int size, bool big_endian>
void
Symbol_table::add_from_object(
Sized_object<size, big_endian>* object,
const elfcpp::Sym<size, big_endian>* syms,
size_t count,
const char* sym_names,
size_t sym_name_size,
Symbol** sympointers)
{
// We take the size from the first object we see.
if (this->get_size() == 0)
this->set_size(size);
if (size != this->get_size() || size != object->target()->get_size())
{
fprintf(stderr, _("%s: %s: mixing 32-bit and 64-bit ELF objects\n"),
program_name, object->name().c_str());
gold_exit(false);
}
const unsigned char* p = reinterpret_cast<const unsigned char*>(syms);
for (size_t i = 0; i < count; ++i)
{
elfcpp::Sym<size, big_endian> sym(p);
unsigned int st_name = sym.get_st_name();
if (st_name >= sym_name_size)
{
fprintf(stderr, _("%s: %s: bad symbol name offset %u at %lu\n"),
program_name, object->name().c_str(), st_name,
static_cast<unsigned long>(i));
gold_exit(false);
}
const char* name = sym_names + st_name;
// In an object file, an '@' in the name separates the symbol
// name from the version name. If there are two '@' characters,
// this is the default version.
const char* ver = strchr(name, '@');
Symbol* res;
if (ver == NULL)
{
name = this->namepool_.add(name);
res = this->add_from_object(object, name, NULL, false, sym);
}
else
{
name = this->namepool_.add(name, ver - name);
bool def = false;
++ver;
if (*ver == '@')
{
def = true;
++ver;
}
ver = this->namepool_.add(ver);
res = this->add_from_object(object, name, ver, def, sym);
}
*sympointers++ = res;
p += elfcpp::Elf_sizes<size>::sym_size;
}
}
// Instantiate the templates we need. We could use the configure
// script to restrict this to only the ones needed for implemented
// targets.
template
void
Symbol_table::add_from_object<32, true>(
Sized_object<32, true>* object,
const elfcpp::Sym<32, true>* syms,
size_t count,
const char* sym_names,
size_t sym_name_size,
Symbol** sympointers);
template
void
Symbol_table::add_from_object<32, false>(
Sized_object<32, false>* object,
const elfcpp::Sym<32, false>* syms,
size_t count,
const char* sym_names,
size_t sym_name_size,
Symbol** sympointers);
template
void
Symbol_table::add_from_object<64, true>(
Sized_object<64, true>* object,
const elfcpp::Sym<64, true>* syms,
size_t count,
const char* sym_names,
size_t sym_name_size,
Symbol** sympointers);
template
void
Symbol_table::add_from_object<64, false>(
Sized_object<64, false>* object,
const elfcpp::Sym<64, false>* syms,
size_t count,
const char* sym_names,
size_t sym_name_size,
Symbol** sympointers);
} // End namespace gold.

View File

@ -3,15 +3,12 @@
// Symbol_table
// The symbol table.
#include "gold.h"
#include <string>
#include <utility>
#include "elfcpp.h"
#include "targetsize.h"
#include "object.h"
#include "workqueue.h"
#include "stringpool.h"
#ifndef GOLD_SYMTAB_H
#define GOLD_SYMTAB_H
@ -19,60 +16,221 @@
namespace gold
{
// An entry in the symbol table. The symbol table can have a lot of
// entries, so we don't want this class to get too big.
class Object;
template<int size, bool big_endian>
class Sized_object;
template<int size, bool big_endian>
class Sized_target;
// The base class of an entry in the symbol table. The symbol table
// can have a lot of entries, so we don't want this class to big.
// Size dependent fields can be found in the template class
// Sized_symbol. Targets may support their own derived classes.
template<int size>
class Symbol
{
public:
typedef typename elfcpp::Elf_types<size>::Elf_Addr Value;
typedef typename Size_types<size>::Unsigned_type Size;
virtual ~Symbol();
// Return the symbol name.
const char*
name() const
{ return this->name_; }
// Return the symbol version. This will return NULL for an
// unversioned symbol.
const char*
version() const
{ return this->version_; }
// Return whether this symbol is a forwarder. This will never be
// true of a symbol found in the hash table, but may be true of
// symbol pointers attached to object files.
bool
is_forwarder() const
{ return this->forwarder_; }
// Mark this symbol as a forwarder.
void
set_forwarder()
{ this->forwarder_ = true; }
// Return the object with which this symbol is associated.
Object*
object() const
{ return this->object_; }
// Return the symbol binding.
elfcpp::STB
binding() const
{ return this->binding_; }
// Return the section index.
unsigned int
shnum() const
{ return this->shnum_; }
protected:
// Instances of this class should always be created at a specific
// size.
Symbol()
{ }
// Initialize fields from an ELF symbol in OBJECT.
template<int size, bool big_endian>
void
init_base(const char *name, const char* version, Object* object,
const elfcpp::Sym<size, big_endian>&);
private:
// Every symbol has a unique name, more or less, so we use
// std::string for the name. There are only a few versions in a
// given link, so for them we point into a pool.
std::string name_;
Symbol(const Symbol&);
Symbol& operator=(const Symbol&);
// Symbol name (expected to point into a Stringpool).
const char* name_;
// Symbol version (expected to point into a Stringpool). This may
// be NULL.
const char* version_;
// Object in which symbol is defined, or in which it was first seen.
Object* object_;
// Section number in object_ in which symbol is defined.
unsigned int shnum_;
Value value_;
Size size_;
// Symbol type.
elfcpp::STT type_ : 4;
// Symbol binding.
elfcpp::STB binding_ : 4;
elfcpp:STV visibility_ : 2;
// Symbol visibility.
elfcpp::STV visibility_ : 2;
// Rest of symbol st_other field.
unsigned int other_ : 6;
// True if this symbol always requires special target-specific
// handling.
bool special_ : 1;
// True if this is the default version of the symbol.
bool def_ : 1;
// True if this symbol really forwards to another symbol. This is
// used when we discover after the fact that two different entries
// in the hash table really refer to the same symbol. This will
// never be set for a symbol found in the hash table, but may be set
// for a symbol found in the list of symbols attached to an Object.
// It forwards to the symbol found in the forwarders_ map of
// Symbol_table.
bool forwarder_ : 1;
};
// The parts of a symbol which are size specific. Using a template
// derived class like this helps us use less space on a 32-bit system.
template<int size>
class Sized_symbol : public Symbol
{
public:
Sized_symbol()
{ }
// Initialize fields from an ELF symbol in OBJECT.
template<bool big_endian>
void
init(const char *name, const char* version, Object* object,
const elfcpp::Sym<size, big_endian>&);
private:
Sized_symbol(const Sized_symbol&);
Sized_symbol& operator=(const Sized_symbol&);
// Symbol value.
typename elfcpp::Elf_types<size>::Elf_Addr value_;
// Symbol size.
typename elfcpp::Elf_types<size>::Elf_WXword size_;
};
// The main linker symbol table.
template<int size>
class Symbol_table
{
public:
Symbol_table();
// Return a pointer to a symbol specified by name.
Symbol*
lookup(const std::string& name) const;
virtual ~Symbol_table();
// Return a pointer to a symbol specified by name plus version.
Symbol*
lookup(const std::string& name, const char* version) const;
// Add COUNT external symbols from OBJECT to the symbol table. SYMS
// is the symbols, SYM_NAMES is their names, SYM_NAME_SIZE is the
// size of SYM_NAMES. This sets SYMPOINTERS to point to the symbols
// in the symbol table.
template<int size, bool big_endian>
void
add_from_object(Sized_object<size, big_endian>* object,
const elfcpp::Sym<size, big_endian>* syms,
size_t count, const char* sym_names, size_t sym_name_size,
Symbol** sympointers);
Task_token&
token() const
{ return this->token_; }
// Return the real symbol associated with the forwarder symbol FROM.
Symbol*
resolve_forwards(Symbol* from) const;
// Return the size of the symbols in the table.
int
get_size() const
{ return this->size_; }
private:
Symbol_table(const Symbol_table&);
Symbol_table& operator=(const Symbol_table&);
typedef std::pair<std::string, std::string> Symbol_table_key;
// Set the size of the symbols in the table.
void
set_size(int size)
{ this->size_ = size; }
Unordered_map<Symbol_table_key, Symbol<size>*> table_;
Task_token token_;
// Make FROM a forwarder symbol to TO.
void
make_forwarder(Symbol* from, Symbol* to);
// Add a symbol.
template<int size, bool big_endian>
Symbol*
add_from_object(Sized_object<size, big_endian>*, const char *name,
const char *version, bool def,
const elfcpp::Sym<size, big_endian>& sym);
// Resolve symbols.
template<int size, bool big_endian>
static void
resolve(Symbol* to, const elfcpp::Sym<size, big_endian>& sym, Object*);
static void
resolve(Symbol* to, const Symbol* from);
typedef std::pair<const char*, const char*> Symbol_table_key;
struct Symbol_table_hash
{
size_t
operator()(const Symbol_table_key&) const;
};
struct Symbol_table_eq
{
bool
operator()(const Symbol_table_key&, const Symbol_table_key&) const;
};
typedef Unordered_map<Symbol_table_key, Symbol*, Symbol_table_hash,
Symbol_table_eq> Symbol_table_type;
// The size of the symbols in the symbol table (32 or 64).
int size_;
// The symbol table itself.
Symbol_table_type table_;
// A pool of symbol names.
Stringpool namepool_;
// Forwarding symbols.
Unordered_map<Symbol*, Symbol*> forwarders_;
};
} // End namespace gold.

52
gold/target-select.cc Normal file
View File

@ -0,0 +1,52 @@
// target-select.cc -- select a target for an object file
#include "gold.h"
#include "elfcpp.h"
#include "target-select.h"
namespace
{
// The start of the list of target selectors.
gold::Target_selector* target_selectors;
} // End anonymous namespace.
namespace gold
{
// Construct a Target_selector, which means adding it to the linked
// list. This runs at global constructor time, so we want it to be
// fast.
Target_selector::Target_selector(int machine, int size, bool big_endian)
: machine_(machine), size_(size), big_endian_(big_endian)
{
this->next_ = target_selectors;
target_selectors = this;
}
// Find the target for an ELF file.
extern Target*
select_target(int machine, int size, bool big_endian, int osabi,
int abiversion)
{
for (const Target_selector* p = target_selectors; p != NULL; p = p->next())
{
int pmach = p->machine();
if ((pmach == machine || pmach == elfcpp::EM_NONE)
&& p->size() == size
&& p->big_endian() ? big_endian : !big_endian)
{
Target* ret = p->recognize(machine, osabi, abiversion);
if (ret != NULL)
return ret;
}
}
return NULL;
}
} // End namespace gold.

69
gold/target-select.h Normal file
View File

@ -0,0 +1,69 @@
// target-select.h -- select a target for an object file -*- C++ -*-
#ifndef GOLD_TARGET_SELECT_H
#define GOLD_TARGET_SELECT_H
namespace gold
{
class Target;
// We want to avoid a master list of targets, which implies using a
// global constructor. And we also want the program to start up as
// quickly as possible, which implies avoiding global constructors.
// We compromise on a very simple global constructor. We use a target
// selector, which specifies an ELF machine number and a recognition
// function. We use global constructors to build a linked list of
// target selectors--a simple pointer list, not a std::list.
class Target_selector
{
public:
// Create a target selector for a specific machine number, size (32
// or 64), and endianness. The machine number can be EM_NONE to
// test for any machine number.
Target_selector(int machine, int size, bool big_endian);
virtual ~Target_selector()
{ }
// If we can handle this target, return a pointer to a target
// structure. The size and endianness are known.
virtual Target* recognize(int machine, int osabi, int abiversion) const = 0;
// Return the next Target_selector in the linked list.
Target_selector*
next() const
{ return this->next_; }
// Return the machine number this selector is looking for, which can
// be EM_NONE to match any machine number.
int
machine() const
{ return this->machine_; }
// Return the size this is looking for (32 or 64).
int
size() const
{ return this->size_; }
// Return the endianness this is looking for.
bool
big_endian() const
{ return this->big_endian_; }
private:
int machine_;
int size_;
bool big_endian_;
Target_selector* next_;
};
// Select the target for an ELF file.
extern Target* select_target(int machine, int size, bool big_endian,
int osabi, int abiversion);
} // End namespace gold.
#endif // !defined(GOLD_TARGET_SELECT_H)

View File

@ -1,4 +1,4 @@
// target.h -- target support for gold
// target.h -- target support for gold -*- C++ -*-
// The abstract class Target is the interface for target specific
// support. It defines abstract methods which each target must
@ -13,16 +13,103 @@
#ifndef GOLD_TARGET_H
#define GOLD_TARGET_H
#include "symtab.h"
#include "elfcpp.h"
namespace gold
{
class Object;
// The abstract class for target specific handling.
class Target
{
public:
virtual ~Target()
{ }
// Return the bit size that this target implements. This should
// return 32 or 64.
int
get_size() const
{ return this->size_; }
// Return whether this target is big-endian.
bool
is_big_endian() const
{ return this->is_big_endian_; }
// Whether this target has a specific make_symbol function.
bool
has_make_symbol() const
{ return this->has_make_symbol_; }
// Whether this target has a specific resolve function.
bool
has_resolve() const
{ return this->has_resolve_; }
// Resolve a symbol. This is called when we see a symbol with a
// target specific binding (STB_LOOS through STB_HIOS or STB_LOPROC
// through STB_HIPROC). TO is a pre-existing symbol. SYM is the
// new symbol, seen in OBJECT. This returns true on success, false
// if the symbol can not be resolved.
template<int size, bool big_endian>
bool
resolve(Sized_symbol<size>* to, const elfcpp::Sym<size, big_endian>& sym,
Object* object);
protected:
Target(int size, bool is_big_endian, bool has_make_symbol, bool has_resolve)
: size_(size),
is_big_endian_(is_big_endian),
has_make_symbol_(has_make_symbol),
has_resolve_(has_resolve)
{ }
private:
Target(const Target&);
Target& operator=(const Target&);
// The target size.
int size_;
// Whether this target is big endian.
bool is_big_endian_;
// Whether this target has a special make_symbol function.
bool has_make_symbol_;
// Whether this target has a special resolve function.
bool has_resolve_;
};
extern Target* select_target(int machine, int size, bool big_endian,
int osabi, int abiversion);
// The abstract class for a specific size and endianness of target.
// Each actual target implementation class should derive from an
// instantiation of Sized_target.
template<int size, bool big_endian>
class Sized_target : public Target
{
public:
// Make a new symbol table entry for the target. This should be
// overridden by a target which needs additional information in the
// symbol table. This will only be called if has_make_symbol()
// returns true.
virtual Sized_symbol<size>*
make_symbol()
{ abort(); }
// Resolve a symbol for the target. This should be overridden by a
// target which needs to take special action. TO is the
// pre-existing symbol. SYM is the new symbol, seen in OBJECT.
virtual void
resolve(Symbol*, const elfcpp::Sym<size, big_endian>&, Object*)
{ abort(); }
protected:
Sized_target(bool has_make_symbol, bool has_resolve)
: Target(size, big_endian, has_make_symbol, has_resolve)
{ }
};
} // End namespace gold.