From Andrew Chatham and Craig Silverstein: Add support for version
scripts.
This commit is contained in:
parent
58da7b1b61
commit
091244672e
20 changed files with 1017 additions and 106 deletions
|
@ -198,6 +198,8 @@ MSGMERGE = @MSGMERGE@
|
||||||
NATIVE_LINKER_FALSE = @NATIVE_LINKER_FALSE@
|
NATIVE_LINKER_FALSE = @NATIVE_LINKER_FALSE@
|
||||||
NATIVE_LINKER_TRUE = @NATIVE_LINKER_TRUE@
|
NATIVE_LINKER_TRUE = @NATIVE_LINKER_TRUE@
|
||||||
NO_WERROR = @NO_WERROR@
|
NO_WERROR = @NO_WERROR@
|
||||||
|
OBJDUMP_AND_CPPFILT_FALSE = @OBJDUMP_AND_CPPFILT_FALSE@
|
||||||
|
OBJDUMP_AND_CPPFILT_TRUE = @OBJDUMP_AND_CPPFILT_TRUE@
|
||||||
OBJEXT = @OBJEXT@
|
OBJEXT = @OBJEXT@
|
||||||
PACKAGE = @PACKAGE@
|
PACKAGE = @PACKAGE@
|
||||||
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
|
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
|
||||||
|
|
21
gold/configure
vendored
21
gold/configure
vendored
|
@ -309,7 +309,7 @@ ac_includes_default="\
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
#endif"
|
#endif"
|
||||||
|
|
||||||
ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar THREADS_TRUE THREADS_FALSE TARGETOBJS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE YACC RANLIB ac_ct_RANLIB LN_S USE_NLS LIBINTL LIBINTL_DEP INCINTL XGETTEXT GMSGFMT POSUB CATALOGS DATADIRNAME INSTOBJEXT GENCAT CATOBJEXT MKINSTALLDIRS MSGFMT MSGMERGE NATIVE_LINKER_TRUE NATIVE_LINKER_FALSE GCC_TRUE GCC_FALSE FN_PTRS_IN_SO_WITHOUT_PIC_TRUE FN_PTRS_IN_SO_WITHOUT_PIC_FALSE TLS_TRUE TLS_FALSE STATIC_TLS_TRUE STATIC_TLS_FALSE WARN_CFLAGS NO_WERROR WARN_CXXFLAGS LFS_CXXFLAGS LIBOBJS CPP EGREP CXXCPP MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT LTLIBOBJS'
|
ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar THREADS_TRUE THREADS_FALSE TARGETOBJS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE YACC RANLIB ac_ct_RANLIB LN_S USE_NLS LIBINTL LIBINTL_DEP INCINTL XGETTEXT GMSGFMT POSUB CATALOGS DATADIRNAME INSTOBJEXT GENCAT CATOBJEXT MKINSTALLDIRS MSGFMT MSGMERGE NATIVE_LINKER_TRUE NATIVE_LINKER_FALSE GCC_TRUE GCC_FALSE OBJDUMP_AND_CPPFILT_TRUE OBJDUMP_AND_CPPFILT_FALSE FN_PTRS_IN_SO_WITHOUT_PIC_TRUE FN_PTRS_IN_SO_WITHOUT_PIC_FALSE TLS_TRUE TLS_FALSE STATIC_TLS_TRUE STATIC_TLS_FALSE WARN_CFLAGS NO_WERROR WARN_CXXFLAGS LFS_CXXFLAGS LIBOBJS CPP EGREP CXXCPP MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT LTLIBOBJS'
|
||||||
ac_subst_files=''
|
ac_subst_files=''
|
||||||
|
|
||||||
# Initialize some variables set by options.
|
# Initialize some variables set by options.
|
||||||
|
@ -4459,6 +4459,16 @@ fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if objdump --help >/dev/null && c++filt --help >/dev/null; then
|
||||||
|
OBJDUMP_AND_CPPFILT_TRUE=
|
||||||
|
OBJDUMP_AND_CPPFILT_FALSE='#'
|
||||||
|
else
|
||||||
|
OBJDUMP_AND_CPPFILT_TRUE='#'
|
||||||
|
OBJDUMP_AND_CPPFILT_FALSE=
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if
|
if
|
||||||
case $target_cpu in
|
case $target_cpu in
|
||||||
|
@ -6388,6 +6398,13 @@ echo "$as_me: error: conditional \"GCC\" was never defined.
|
||||||
Usually this means the macro was only invoked conditionally." >&2;}
|
Usually this means the macro was only invoked conditionally." >&2;}
|
||||||
{ (exit 1); exit 1; }; }
|
{ (exit 1); exit 1; }; }
|
||||||
fi
|
fi
|
||||||
|
if test -z "${OBJDUMP_AND_CPPFILT_TRUE}" && test -z "${OBJDUMP_AND_CPPFILT_FALSE}"; then
|
||||||
|
{ { echo "$as_me:$LINENO: error: conditional \"OBJDUMP_AND_CPPFILT\" was never defined.
|
||||||
|
Usually this means the macro was only invoked conditionally." >&5
|
||||||
|
echo "$as_me: error: conditional \"OBJDUMP_AND_CPPFILT\" was never defined.
|
||||||
|
Usually this means the macro was only invoked conditionally." >&2;}
|
||||||
|
{ (exit 1); exit 1; }; }
|
||||||
|
fi
|
||||||
if test -z "${FN_PTRS_IN_SO_WITHOUT_PIC_TRUE}" && test -z "${FN_PTRS_IN_SO_WITHOUT_PIC_FALSE}"; then
|
if test -z "${FN_PTRS_IN_SO_WITHOUT_PIC_TRUE}" && test -z "${FN_PTRS_IN_SO_WITHOUT_PIC_FALSE}"; then
|
||||||
{ { echo "$as_me:$LINENO: error: conditional \"FN_PTRS_IN_SO_WITHOUT_PIC\" was never defined.
|
{ { echo "$as_me:$LINENO: error: conditional \"FN_PTRS_IN_SO_WITHOUT_PIC\" was never defined.
|
||||||
Usually this means the macro was only invoked conditionally." >&5
|
Usually this means the macro was only invoked conditionally." >&5
|
||||||
|
@ -7040,6 +7057,8 @@ s,@NATIVE_LINKER_TRUE@,$NATIVE_LINKER_TRUE,;t t
|
||||||
s,@NATIVE_LINKER_FALSE@,$NATIVE_LINKER_FALSE,;t t
|
s,@NATIVE_LINKER_FALSE@,$NATIVE_LINKER_FALSE,;t t
|
||||||
s,@GCC_TRUE@,$GCC_TRUE,;t t
|
s,@GCC_TRUE@,$GCC_TRUE,;t t
|
||||||
s,@GCC_FALSE@,$GCC_FALSE,;t t
|
s,@GCC_FALSE@,$GCC_FALSE,;t t
|
||||||
|
s,@OBJDUMP_AND_CPPFILT_TRUE@,$OBJDUMP_AND_CPPFILT_TRUE,;t t
|
||||||
|
s,@OBJDUMP_AND_CPPFILT_FALSE@,$OBJDUMP_AND_CPPFILT_FALSE,;t t
|
||||||
s,@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@,$FN_PTRS_IN_SO_WITHOUT_PIC_TRUE,;t t
|
s,@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@,$FN_PTRS_IN_SO_WITHOUT_PIC_TRUE,;t t
|
||||||
s,@FN_PTRS_IN_SO_WITHOUT_PIC_FALSE@,$FN_PTRS_IN_SO_WITHOUT_PIC_FALSE,;t t
|
s,@FN_PTRS_IN_SO_WITHOUT_PIC_FALSE@,$FN_PTRS_IN_SO_WITHOUT_PIC_FALSE,;t t
|
||||||
s,@TLS_TRUE@,$TLS_TRUE,;t t
|
s,@TLS_TRUE@,$TLS_TRUE,;t t
|
||||||
|
|
|
@ -157,6 +157,8 @@ AC_EXEEXT
|
||||||
AM_CONDITIONAL(NATIVE_LINKER,
|
AM_CONDITIONAL(NATIVE_LINKER,
|
||||||
test "x$target_alias" = "x" -o "x$host_alias" = "x$target_alias")
|
test "x$target_alias" = "x" -o "x$host_alias" = "x$target_alias")
|
||||||
AM_CONDITIONAL(GCC, test "$GCC" = yes)
|
AM_CONDITIONAL(GCC, test "$GCC" = yes)
|
||||||
|
AM_CONDITIONAL(OBJDUMP_AND_CPPFILT,
|
||||||
|
[objdump --help >/dev/null && c++filt --help >/dev/null])
|
||||||
|
|
||||||
dnl Some architectures do not support taking pointers of functions
|
dnl Some architectures do not support taking pointers of functions
|
||||||
dnl defined in shared libraries except in -fPIC mode. We need to
|
dnl defined in shared libraries except in -fPIC mode. We need to
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// dynobj.cc -- dynamic object support for gold
|
// dynobj.cc -- dynamic object support for gold
|
||||||
|
|
||||||
// Copyright 2006, 2007 Free Software Foundation, Inc.
|
// Copyright 2006, 2007, 2008 Free Software Foundation, Inc.
|
||||||
// Written by Ian Lance Taylor <iant@google.com>.
|
// Written by Ian Lance Taylor <iant@google.com>.
|
||||||
|
|
||||||
// This file is part of gold.
|
// This file is part of gold.
|
||||||
|
@ -1227,6 +1227,46 @@ Verneed::write(const Stringpool* dynpool, bool is_last,
|
||||||
|
|
||||||
// Versions methods.
|
// Versions methods.
|
||||||
|
|
||||||
|
Versions::Versions(const General_options& options, Stringpool* dynpool)
|
||||||
|
: defs_(), needs_(), version_table_(),
|
||||||
|
is_finalized_(false), version_script_(options.version_script())
|
||||||
|
{
|
||||||
|
// We always need a base version, so define that first. Nothing
|
||||||
|
// explicitly declares itself as part of base, so it doesn't need to
|
||||||
|
// be in version_table_.
|
||||||
|
// FIXME: Should use soname here when creating a shared object. Is
|
||||||
|
// this fixme still valid? It looks like it's doing the right thing
|
||||||
|
// to me.
|
||||||
|
if (parameters->output_is_shared())
|
||||||
|
{
|
||||||
|
const char* name = dynpool->add(parameters->output_file_name(),
|
||||||
|
false, NULL);
|
||||||
|
Verdef* vdbase = new Verdef(name, std::vector<std::string>(),
|
||||||
|
true, false, true);
|
||||||
|
this->defs_.push_back(vdbase);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->version_script_.empty())
|
||||||
|
{
|
||||||
|
// Parse the version script, and insert each declared version into
|
||||||
|
// defs_ and version_table_.
|
||||||
|
std::vector<std::string> versions = this->version_script_.get_versions();
|
||||||
|
for (size_t k = 0; k < versions.size(); ++k)
|
||||||
|
{
|
||||||
|
Stringpool::Key version_key;
|
||||||
|
const char* version = dynpool->add(versions[k].c_str(),
|
||||||
|
true, &version_key);
|
||||||
|
Verdef* const vd = new Verdef(
|
||||||
|
version,
|
||||||
|
options.version_script().get_dependencies(version),
|
||||||
|
false, false, false);
|
||||||
|
this->defs_.push_back(vd);
|
||||||
|
Key key(version_key, 0);
|
||||||
|
this->version_table_.insert(std::make_pair(key, vd));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Versions::~Versions()
|
Versions::~Versions()
|
||||||
{
|
{
|
||||||
for (Defs::iterator p = this->defs_.begin();
|
for (Defs::iterator p = this->defs_.begin();
|
||||||
|
@ -1265,7 +1305,7 @@ Versions::record_version(const Symbol_table* symtab,
|
||||||
{
|
{
|
||||||
gold_assert(!this->is_finalized_);
|
gold_assert(!this->is_finalized_);
|
||||||
gold_assert(sym->version() != NULL);
|
gold_assert(sym->version() != NULL);
|
||||||
|
|
||||||
Stringpool::Key version_key;
|
Stringpool::Key version_key;
|
||||||
const char* version = dynpool->add(sym->version(), false, &version_key);
|
const char* version = dynpool->add(sym->version(), false, &version_key);
|
||||||
|
|
||||||
|
@ -1292,7 +1332,7 @@ Versions::add_def(const Symbol* sym, const char* version,
|
||||||
Version_base* const vbnull = NULL;
|
Version_base* const vbnull = NULL;
|
||||||
std::pair<Version_table::iterator, bool> ins =
|
std::pair<Version_table::iterator, bool> ins =
|
||||||
this->version_table_.insert(std::make_pair(k, vbnull));
|
this->version_table_.insert(std::make_pair(k, vbnull));
|
||||||
|
|
||||||
if (!ins.second)
|
if (!ins.second)
|
||||||
{
|
{
|
||||||
// We already have an entry for this version.
|
// We already have an entry for this version.
|
||||||
|
@ -1318,16 +1358,10 @@ Versions::add_def(const Symbol* sym, const char* version,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is the first version we are defining, first define
|
|
||||||
// the base version. FIXME: Should use soname here when
|
|
||||||
// creating a shared object.
|
|
||||||
Verdef* vdbase = new Verdef(parameters->output_file_name(), true, false,
|
|
||||||
true);
|
|
||||||
this->defs_.push_back(vdbase);
|
|
||||||
|
|
||||||
// When creating a regular executable, automatically define
|
// When creating a regular executable, automatically define
|
||||||
// a new version.
|
// a new version.
|
||||||
Verdef* vd = new Verdef(version, false, false, false);
|
Verdef* vd = new Verdef(version, std::vector<std::string>(),
|
||||||
|
false, false, false);
|
||||||
this->defs_.push_back(vd);
|
this->defs_.push_back(vd);
|
||||||
ins.first->second = vd;
|
ins.first->second = vd;
|
||||||
}
|
}
|
||||||
|
@ -1499,10 +1533,14 @@ Versions::symbol_section_contents(const Symbol_table* symtab,
|
||||||
const char* version = (*p)->version();
|
const char* version = (*p)->version();
|
||||||
if (version == NULL)
|
if (version == NULL)
|
||||||
version_index = elfcpp::VER_NDX_GLOBAL;
|
version_index = elfcpp::VER_NDX_GLOBAL;
|
||||||
else
|
else
|
||||||
version_index = this->version_index(symtab, dynpool, *p);
|
version_index = this->version_index(symtab, dynpool, *p);
|
||||||
|
// If the symbol was defined as foo@V1 instead of foo@@V1, add
|
||||||
|
// the hidden bit.
|
||||||
|
if ((*p)->version() != NULL && !(*p)->is_default())
|
||||||
|
version_index |= elfcpp::VERSYM_HIDDEN;
|
||||||
elfcpp::Swap<16, big_endian>::writeval(pbuf + (*p)->dynsym_index() * 2,
|
elfcpp::Swap<16, big_endian>::writeval(pbuf + (*p)->dynsym_index() * 2,
|
||||||
version_index);
|
version_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
*pp = pbuf;
|
*pp = pbuf;
|
||||||
|
|
|
@ -32,6 +32,7 @@ namespace gold
|
||||||
{
|
{
|
||||||
|
|
||||||
class General_options;
|
class General_options;
|
||||||
|
class Version_script_info;
|
||||||
|
|
||||||
// A dynamic object (ET_DYN). This is an abstract base class itself.
|
// A dynamic object (ET_DYN). This is an abstract base class itself.
|
||||||
// The implementations is the template class Sized_dynobj.
|
// The implementations is the template class Sized_dynobj.
|
||||||
|
@ -309,8 +310,9 @@ class Version_base
|
||||||
class Verdef : public Version_base
|
class Verdef : public Version_base
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Verdef(const char* name, bool is_base, bool is_weak, bool is_symbol_created)
|
Verdef(const char* name, const std::vector<std::string>& deps,
|
||||||
: name_(name), deps_(), is_base_(is_base), is_weak_(is_weak),
|
bool is_base, bool is_weak, bool is_symbol_created)
|
||||||
|
: name_(name), deps_(deps), is_base_(is_base), is_weak_(is_weak),
|
||||||
is_symbol_created_(is_symbol_created)
|
is_symbol_created_(is_symbol_created)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
@ -358,7 +360,7 @@ class Verdef : public Version_base
|
||||||
|
|
||||||
// The type of the list of version dependencies. Each dependency
|
// The type of the list of version dependencies. Each dependency
|
||||||
// should be canonicalized in the dynamic Stringpool.
|
// should be canonicalized in the dynamic Stringpool.
|
||||||
typedef std::vector<const char*> Deps;
|
typedef std::vector<std::string> Deps;
|
||||||
|
|
||||||
// The name of this version. This should be canonicalized in the
|
// The name of this version. This should be canonicalized in the
|
||||||
// dynamic Stringpool.
|
// dynamic Stringpool.
|
||||||
|
@ -459,9 +461,7 @@ class Verneed
|
||||||
class Versions
|
class Versions
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Versions()
|
Versions(const General_options&, Stringpool*);
|
||||||
: defs_(), needs_(), version_table_(), is_finalized_(false)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
~Versions();
|
~Versions();
|
||||||
|
|
||||||
|
@ -513,7 +513,14 @@ class Versions
|
||||||
unsigned int* psize, unsigned int* pentries
|
unsigned int* psize, unsigned int* pentries
|
||||||
ACCEPT_SIZE_ENDIAN) const;
|
ACCEPT_SIZE_ENDIAN) const;
|
||||||
|
|
||||||
|
const Version_script_info&
|
||||||
|
version_script() const
|
||||||
|
{ return this->version_script_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Versions(const Versions&);
|
||||||
|
Versions& operator=(const Versions&);
|
||||||
|
|
||||||
// The type of the list of version definitions.
|
// The type of the list of version definitions.
|
||||||
typedef std::vector<Verdef*> Defs;
|
typedef std::vector<Verdef*> Defs;
|
||||||
|
|
||||||
|
@ -568,6 +575,8 @@ class Versions
|
||||||
Version_table version_table_;
|
Version_table version_table_;
|
||||||
// Whether the version indexes have been set.
|
// Whether the version indexes have been set.
|
||||||
bool is_finalized_;
|
bool is_finalized_;
|
||||||
|
// Contents of --version-script, if passed, or NULL.
|
||||||
|
const Version_script_info& version_script_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // End namespace gold.
|
} // End namespace gold.
|
||||||
|
|
|
@ -683,7 +683,7 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab,
|
||||||
Output_section* dynstr;
|
Output_section* dynstr;
|
||||||
std::vector<Symbol*> dynamic_symbols;
|
std::vector<Symbol*> dynamic_symbols;
|
||||||
unsigned int local_dynamic_count;
|
unsigned int local_dynamic_count;
|
||||||
Versions versions;
|
Versions versions(this->options_, &this->dynpool_);
|
||||||
this->create_dynamic_symtab(input_objects, target, symtab, &dynstr,
|
this->create_dynamic_symtab(input_objects, target, symtab, &dynstr,
|
||||||
&local_dynamic_count, &dynamic_symbols,
|
&local_dynamic_count, &dynamic_symbols,
|
||||||
&versions);
|
&versions);
|
||||||
|
|
|
@ -84,7 +84,8 @@ main(int argc, char** argv)
|
||||||
// we're going to see based on the number of input files. Even when
|
// we're going to see based on the number of input files. Even when
|
||||||
// this is off, it means at worse we don't quite optimize hashtable
|
// this is off, it means at worse we don't quite optimize hashtable
|
||||||
// resizing as well as we could have (perhap using more memory).
|
// resizing as well as we could have (perhap using more memory).
|
||||||
Symbol_table symtab(command_line.number_of_input_files() * 1024);
|
Symbol_table symtab(command_line.number_of_input_files() * 1024,
|
||||||
|
command_line.options().version_script());
|
||||||
|
|
||||||
// The layout object.
|
// The layout object.
|
||||||
Layout layout(command_line.options(), &script_options);
|
Layout layout(command_line.options(), &script_options);
|
||||||
|
|
|
@ -155,7 +155,23 @@ invoke_script(int argc, char** argv, char* arg, bool long_option,
|
||||||
arg, long_option,
|
arg, long_option,
|
||||||
&ret);
|
&ret);
|
||||||
if (!read_commandline_script(script_name, cmdline))
|
if (!read_commandline_script(script_name, cmdline))
|
||||||
gold::gold_error(_("unable to parse script file %s"), script_name);
|
gold::gold_fatal(_("unable to parse script file %s"), script_name);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the special --version-script option, which reads a version script.
|
||||||
|
|
||||||
|
int
|
||||||
|
invoke_version_script(int argc, char** argv, char* arg, bool long_option,
|
||||||
|
gold::Command_line* cmdline)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
const char* script_name = cmdline->get_special_argument("version-script",
|
||||||
|
argc, argv,
|
||||||
|
arg, long_option,
|
||||||
|
&ret);
|
||||||
|
if (!read_version_script(script_name, cmdline))
|
||||||
|
gold::gold_fatal(_("unable to parse version script file %s"), script_name);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,6 +474,9 @@ options::Command_line_options::options[] =
|
||||||
SPECIAL('T', "script", N_("Read linker script"),
|
SPECIAL('T', "script", N_("Read linker script"),
|
||||||
N_("-T FILE, --script FILE"), TWO_DASHES,
|
N_("-T FILE, --script FILE"), TWO_DASHES,
|
||||||
&invoke_script),
|
&invoke_script),
|
||||||
|
SPECIAL('\0', "version-script", N_("Read version script"),
|
||||||
|
N_("--version-script FILE"), TWO_DASHES,
|
||||||
|
&invoke_version_script),
|
||||||
GENERAL_NOARG('\0', "threads", N_("Run the linker multi-threaded"),
|
GENERAL_NOARG('\0', "threads", N_("Run the linker multi-threaded"),
|
||||||
NULL, TWO_DASHES, &General_options::set_threads),
|
NULL, TWO_DASHES, &General_options::set_threads),
|
||||||
GENERAL_NOARG('\0', "no-threads", N_("Do not run the linker multi-threaded"),
|
GENERAL_NOARG('\0', "no-threads", N_("Do not run the linker multi-threaded"),
|
||||||
|
|
|
@ -233,6 +233,11 @@ class General_options
|
||||||
sysroot() const
|
sysroot() const
|
||||||
{ return this->sysroot_; }
|
{ return this->sysroot_; }
|
||||||
|
|
||||||
|
// --version-script: The version script to apply if --shared is true.
|
||||||
|
const Version_script_info&
|
||||||
|
version_script() const
|
||||||
|
{ return *this->script_options_->version_script_info(); }
|
||||||
|
|
||||||
// -Ttext: The address of the .text section
|
// -Ttext: The address of the .text section
|
||||||
uint64_t
|
uint64_t
|
||||||
text_segment_address() const
|
text_segment_address() const
|
||||||
|
|
|
@ -107,6 +107,12 @@ script_parse_option(void* closure, const char*, size_t);
|
||||||
extern void
|
extern void
|
||||||
script_push_lex_into_expression_mode(void* closure);
|
script_push_lex_into_expression_mode(void* closure);
|
||||||
|
|
||||||
|
/* Called by the bison parser to push the lexer into version
|
||||||
|
mode. */
|
||||||
|
|
||||||
|
extern void
|
||||||
|
script_push_lex_into_version_mode(void* closure);
|
||||||
|
|
||||||
/* Called by the bison parser to pop the lexer mode. */
|
/* Called by the bison parser to pop the lexer mode. */
|
||||||
|
|
||||||
extern void
|
extern void
|
||||||
|
@ -208,6 +214,38 @@ script_exp_function_segment_start(const char*, size_t, Expression_ptr);
|
||||||
extern Expression_ptr
|
extern Expression_ptr
|
||||||
script_exp_function_assert(Expression_ptr, const char*, size_t);
|
script_exp_function_assert(Expression_ptr, const char*, size_t);
|
||||||
|
|
||||||
|
struct Version_dependency_list;
|
||||||
|
struct Version_expression_list;
|
||||||
|
struct Version_tree;
|
||||||
|
|
||||||
|
extern void
|
||||||
|
script_register_vers_node(void* closure,
|
||||||
|
const char* tag,
|
||||||
|
int taglen,
|
||||||
|
struct Version_tree *,
|
||||||
|
struct Version_dependency_list *);
|
||||||
|
|
||||||
|
extern struct Version_dependency_list *
|
||||||
|
script_add_vers_depend(void* closure,
|
||||||
|
struct Version_dependency_list *existing_dependencies,
|
||||||
|
const char *depend_to_add, int deplen);
|
||||||
|
|
||||||
|
extern struct Version_expression_list *
|
||||||
|
script_new_vers_pattern(void* closure,
|
||||||
|
struct Version_expression_list *,
|
||||||
|
const char *, int);
|
||||||
|
|
||||||
|
extern struct Version_tree *
|
||||||
|
script_new_vers_node(void* closure,
|
||||||
|
struct Version_expression_list *global,
|
||||||
|
struct Version_expression_list *local);
|
||||||
|
|
||||||
|
extern void
|
||||||
|
version_script_push_lang(void* closure, const char* lang, int langlen);
|
||||||
|
|
||||||
|
extern void
|
||||||
|
version_script_pop_lang(void* closure);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
475
gold/script.cc
475
gold/script.cc
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
#include "gold.h"
|
#include "gold.h"
|
||||||
|
|
||||||
|
#include <fnmatch.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
@ -29,6 +30,7 @@
|
||||||
#include "filenames.h"
|
#include "filenames.h"
|
||||||
|
|
||||||
#include "elfcpp.h"
|
#include "elfcpp.h"
|
||||||
|
#include "demangle.h"
|
||||||
#include "dirsearch.h"
|
#include "dirsearch.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "fileread.h"
|
#include "fileread.h"
|
||||||
|
@ -245,26 +247,32 @@ class Lex
|
||||||
inline bool
|
inline bool
|
||||||
can_start_name(char c, char c2);
|
can_start_name(char c, char c2);
|
||||||
|
|
||||||
// Return whether C can appear in a name which has already started.
|
// If C can appear in a name which has already started, return a
|
||||||
inline bool
|
// pointer to a character later in the token or just past
|
||||||
can_continue_name(char c);
|
// it. Otherwise, return NULL.
|
||||||
|
inline const char*
|
||||||
|
can_continue_name(const char* c);
|
||||||
|
|
||||||
// Return whether C, C2, C3 can start a hex number.
|
// Return whether C, C2, C3 can start a hex number.
|
||||||
inline bool
|
inline bool
|
||||||
can_start_hex(char c, char c2, char c3);
|
can_start_hex(char c, char c2, char c3);
|
||||||
|
|
||||||
// Return whether C can appear in a hex number.
|
// If C can appear in a hex number which has already started, return
|
||||||
inline bool
|
// a pointer to a character later in the token or just past
|
||||||
can_continue_hex(char c);
|
// it. Otherwise, return NULL.
|
||||||
|
inline const char*
|
||||||
|
can_continue_hex(const char* c);
|
||||||
|
|
||||||
// Return whether C can start a non-hex number.
|
// Return whether C can start a non-hex number.
|
||||||
static inline bool
|
static inline bool
|
||||||
can_start_number(char c);
|
can_start_number(char c);
|
||||||
|
|
||||||
// Return whether C can appear in a non-hex number.
|
// If C can appear in a decimal number which has already started,
|
||||||
inline bool
|
// return a pointer to a character later in the token or just past
|
||||||
can_continue_number(char c)
|
// it. Otherwise, return NULL.
|
||||||
{ return Lex::can_start_number(c); }
|
inline const char*
|
||||||
|
can_continue_number(const char* c)
|
||||||
|
{ return Lex::can_start_number(*c) ? c + 1 : NULL; }
|
||||||
|
|
||||||
// If C1 C2 C3 form a valid three character operator, return the
|
// If C1 C2 C3 form a valid three character operator, return the
|
||||||
// opcode. Otherwise return 0.
|
// opcode. Otherwise return 0.
|
||||||
|
@ -299,7 +307,7 @@ class Lex
|
||||||
// MATCH. Set *PP to the character following the token.
|
// MATCH. Set *PP to the character following the token.
|
||||||
inline Token
|
inline Token
|
||||||
gather_token(Token::Classification,
|
gather_token(Token::Classification,
|
||||||
bool (Lex::*can_continue_fn)(char),
|
const char* (Lex::*can_continue_fn)(const char*),
|
||||||
const char* start, const char* match, const char** pp);
|
const char* start, const char* match, const char** pp);
|
||||||
|
|
||||||
// Build a token from a quoted string.
|
// Build a token from a quoted string.
|
||||||
|
@ -382,7 +390,10 @@ Lex::can_start_name(char c, char c2)
|
||||||
return this->mode_ == LINKER_SCRIPT;
|
return this->mode_ == LINKER_SCRIPT;
|
||||||
|
|
||||||
case '~':
|
case '~':
|
||||||
return this->mode_ == LINKER_SCRIPT && can_continue_name(c2);
|
return this->mode_ == LINKER_SCRIPT && can_continue_name(&c2);
|
||||||
|
|
||||||
|
case '*': case '[':
|
||||||
|
return this->mode_ == VERSION_SCRIPT;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@ -395,10 +406,10 @@ Lex::can_start_name(char c, char c2)
|
||||||
// script language requires spaces around operators, unless we know
|
// script language requires spaces around operators, unless we know
|
||||||
// that we are parsing an expression.
|
// that we are parsing an expression.
|
||||||
|
|
||||||
inline bool
|
inline const char*
|
||||||
Lex::can_continue_name(char c)
|
Lex::can_continue_name(const char* c)
|
||||||
{
|
{
|
||||||
switch (c)
|
switch (*c)
|
||||||
{
|
{
|
||||||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
||||||
case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
|
case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
|
||||||
|
@ -413,16 +424,38 @@ Lex::can_continue_name(char c)
|
||||||
case '_': case '.': case '$':
|
case '_': case '.': case '$':
|
||||||
case '0': case '1': case '2': case '3': case '4':
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
case '5': case '6': case '7': case '8': case '9':
|
case '5': case '6': case '7': case '8': case '9':
|
||||||
return true;
|
return c + 1;
|
||||||
|
|
||||||
case '/': case '\\': case '~':
|
case '/': case '\\': case '~':
|
||||||
case '=': case '+': case '-':
|
case '=': case '+':
|
||||||
case ':': case '[': case ']':
|
case ',': case '?':
|
||||||
case ',': case '?': case '*':
|
if (this->mode_ == LINKER_SCRIPT)
|
||||||
return this->mode_ == LINKER_SCRIPT;
|
return c + 1;
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
case '[': case ']': case '*': case '-':
|
||||||
|
if (this->mode_ == LINKER_SCRIPT || this->mode_ == VERSION_SCRIPT)
|
||||||
|
return c + 1;
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
case '^':
|
||||||
|
if (this->mode_ == VERSION_SCRIPT)
|
||||||
|
return c + 1;
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
case ':':
|
||||||
|
if (this->mode_ == LINKER_SCRIPT)
|
||||||
|
return c + 1;
|
||||||
|
else if (this->mode_ == VERSION_SCRIPT && (c[1] == ':'))
|
||||||
|
{
|
||||||
|
// A name can have '::' in it, as that's a c++ namespace
|
||||||
|
// separator. But a single colon is not part of a name.
|
||||||
|
return c + 2;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,25 +472,25 @@ inline bool
|
||||||
Lex::can_start_hex(char c1, char c2, char c3)
|
Lex::can_start_hex(char c1, char c2, char c3)
|
||||||
{
|
{
|
||||||
if (c1 == '0' && (c2 == 'x' || c2 == 'X'))
|
if (c1 == '0' && (c2 == 'x' || c2 == 'X'))
|
||||||
return this->can_continue_hex(c3);
|
return this->can_continue_hex(&c3);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return whether C can appear in a hex number.
|
// Return whether C can appear in a hex number.
|
||||||
|
|
||||||
inline bool
|
inline const char*
|
||||||
Lex::can_continue_hex(char c)
|
Lex::can_continue_hex(const char* c)
|
||||||
{
|
{
|
||||||
switch (c)
|
switch (*c)
|
||||||
{
|
{
|
||||||
case '0': case '1': case '2': case '3': case '4':
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
case '5': case '6': case '7': case '8': case '9':
|
case '5': case '6': case '7': case '8': case '9':
|
||||||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
||||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
||||||
return true;
|
return c + 1;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -652,13 +685,14 @@ Lex::skip_line_comment(const char** pp)
|
||||||
|
|
||||||
inline Token
|
inline Token
|
||||||
Lex::gather_token(Token::Classification classification,
|
Lex::gather_token(Token::Classification classification,
|
||||||
bool (Lex::*can_continue_fn)(char),
|
const char* (Lex::*can_continue_fn)(const char*),
|
||||||
const char* start,
|
const char* start,
|
||||||
const char* match,
|
const char* match,
|
||||||
const char **pp)
|
const char **pp)
|
||||||
{
|
{
|
||||||
while ((this->*can_continue_fn)(*match))
|
const char* new_match = NULL;
|
||||||
++match;
|
while ((new_match = (this->*can_continue_fn)(match)))
|
||||||
|
match = new_match;
|
||||||
*pp = match;
|
*pp = match;
|
||||||
return this->make_token(classification, start, match - start, start);
|
return this->make_token(classification, start, match - start, start);
|
||||||
}
|
}
|
||||||
|
@ -941,8 +975,13 @@ class Parser_closure
|
||||||
: filename_(filename), posdep_options_(posdep_options),
|
: filename_(filename), posdep_options_(posdep_options),
|
||||||
in_group_(in_group), is_in_sysroot_(is_in_sysroot),
|
in_group_(in_group), is_in_sysroot_(is_in_sysroot),
|
||||||
command_line_(command_line), script_options_(script_options),
|
command_line_(command_line), script_options_(script_options),
|
||||||
|
version_script_info_(script_options->version_script_info()),
|
||||||
lex_(lex), lineno_(0), charpos_(0), lex_mode_stack_(), inputs_(NULL)
|
lex_(lex), lineno_(0), charpos_(0), lex_mode_stack_(), inputs_(NULL)
|
||||||
{ }
|
{
|
||||||
|
// We start out processing C symbols in the default lex mode.
|
||||||
|
language_stack_.push_back("");
|
||||||
|
lex_mode_stack_.push_back(lex->mode());
|
||||||
|
}
|
||||||
|
|
||||||
// Return the file name.
|
// Return the file name.
|
||||||
const char*
|
const char*
|
||||||
|
@ -978,6 +1017,11 @@ class Parser_closure
|
||||||
script_options()
|
script_options()
|
||||||
{ return this->script_options_; }
|
{ return this->script_options_; }
|
||||||
|
|
||||||
|
// Return the object in which version script information should be stored.
|
||||||
|
Version_script_info*
|
||||||
|
version_script()
|
||||||
|
{ return this->version_script_info_; }
|
||||||
|
|
||||||
// Return the next token, and advance.
|
// Return the next token, and advance.
|
||||||
const Token*
|
const Token*
|
||||||
next_token()
|
next_token()
|
||||||
|
@ -1005,6 +1049,11 @@ class Parser_closure
|
||||||
this->lex_mode_stack_.pop_back();
|
this->lex_mode_stack_.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the current lexer mode.
|
||||||
|
Lex::Mode
|
||||||
|
lex_mode() const
|
||||||
|
{ return this->lex_mode_stack_.back(); }
|
||||||
|
|
||||||
// Return the line number of the last token.
|
// Return the line number of the last token.
|
||||||
int
|
int
|
||||||
lineno() const
|
lineno() const
|
||||||
|
@ -1030,6 +1079,23 @@ class Parser_closure
|
||||||
saw_inputs() const
|
saw_inputs() const
|
||||||
{ return this->inputs_ != NULL && !this->inputs_->empty(); }
|
{ return this->inputs_ != NULL && !this->inputs_->empty(); }
|
||||||
|
|
||||||
|
// Return the current language being processed in a version script
|
||||||
|
// (eg, "C++"). The empty string represents unmangled C names.
|
||||||
|
const std::string&
|
||||||
|
get_current_language() const
|
||||||
|
{ return this->language_stack_.back(); }
|
||||||
|
|
||||||
|
// Push a language onto the stack when entering an extern block.
|
||||||
|
void push_language(const std::string& lang)
|
||||||
|
{ this->language_stack_.push_back(lang); }
|
||||||
|
|
||||||
|
// Pop a language off of the stack when exiting an extern block.
|
||||||
|
void pop_language()
|
||||||
|
{
|
||||||
|
gold_assert(!this->language_stack_.empty());
|
||||||
|
this->language_stack_.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// The name of the file we are reading.
|
// The name of the file we are reading.
|
||||||
const char* filename_;
|
const char* filename_;
|
||||||
|
@ -1043,6 +1109,8 @@ class Parser_closure
|
||||||
Command_line* command_line_;
|
Command_line* command_line_;
|
||||||
// Options which may be set from any linker script.
|
// Options which may be set from any linker script.
|
||||||
Script_options* script_options_;
|
Script_options* script_options_;
|
||||||
|
// Information parsed from a version script.
|
||||||
|
Version_script_info* version_script_info_;
|
||||||
// The lexer.
|
// The lexer.
|
||||||
Lex* lex_;
|
Lex* lex_;
|
||||||
// The line number of the last token returned by next_token.
|
// The line number of the last token returned by next_token.
|
||||||
|
@ -1051,6 +1119,9 @@ class Parser_closure
|
||||||
int charpos_;
|
int charpos_;
|
||||||
// A stack of lexer modes.
|
// A stack of lexer modes.
|
||||||
std::vector<Lex::Mode> lex_mode_stack_;
|
std::vector<Lex::Mode> lex_mode_stack_;
|
||||||
|
// A stack of which extern/language block we're inside. Can be C++,
|
||||||
|
// java, or empty for C.
|
||||||
|
std::vector<std::string> language_stack_;
|
||||||
// New input files found to add to the link.
|
// New input files found to add to the link.
|
||||||
Input_arguments* inputs_;
|
Input_arguments* inputs_;
|
||||||
};
|
};
|
||||||
|
@ -1119,11 +1190,13 @@ read_input_script(Workqueue* workqueue, const General_options& options,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FILENAME was found as an argument to --script (-T).
|
// Helper function for read_version_script() and
|
||||||
// Read it as a script, and execute its contents immediately.
|
// read_commandline_script(). Processes the given file in the mode
|
||||||
|
// indicated by first_token and lex_mode.
|
||||||
|
|
||||||
bool
|
static bool
|
||||||
read_commandline_script(const char* filename, Command_line* cmdline)
|
read_script_file(const char* filename, Command_line* cmdline,
|
||||||
|
int first_token, Lex::Mode lex_mode)
|
||||||
{
|
{
|
||||||
// TODO: if filename is a relative filename, search for it manually
|
// TODO: if filename is a relative filename, search for it manually
|
||||||
// using "." + cmdline->options()->search_path() -- not dirsearch.
|
// using "." + cmdline->options()->search_path() -- not dirsearch.
|
||||||
|
@ -1143,7 +1216,8 @@ read_commandline_script(const char* filename, Command_line* cmdline)
|
||||||
std::string input_string;
|
std::string input_string;
|
||||||
Lex::read_file(&input_file, &input_string);
|
Lex::read_file(&input_file, &input_string);
|
||||||
|
|
||||||
Lex lex(input_string.c_str(), input_string.length(), PARSING_LINKER_SCRIPT);
|
Lex lex(input_string.c_str(), input_string.length(), first_token);
|
||||||
|
lex.set_mode(lex_mode);
|
||||||
|
|
||||||
Parser_closure closure(filename,
|
Parser_closure closure(filename,
|
||||||
cmdline->position_dependent_options(),
|
cmdline->position_dependent_options(),
|
||||||
|
@ -1165,6 +1239,27 @@ read_commandline_script(const char* filename, Command_line* cmdline)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FILENAME was found as an argument to --script (-T).
|
||||||
|
// Read it as a script, and execute its contents immediately.
|
||||||
|
|
||||||
|
bool
|
||||||
|
read_commandline_script(const char* filename, Command_line* cmdline)
|
||||||
|
{
|
||||||
|
return read_script_file(filename, cmdline,
|
||||||
|
PARSING_LINKER_SCRIPT, Lex::LINKER_SCRIPT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE was found as an argument to --version-script. Read it as a
|
||||||
|
// version script, and store its contents in
|
||||||
|
// cmdline->script_options()->version_script_info().
|
||||||
|
|
||||||
|
bool
|
||||||
|
read_version_script(const char* filename, Command_line* cmdline)
|
||||||
|
{
|
||||||
|
return read_script_file(filename, cmdline,
|
||||||
|
PARSING_VERSION_SCRIPT, Lex::VERSION_SCRIPT);
|
||||||
|
}
|
||||||
|
|
||||||
// Implement the --defsym option on the command line. Return true if
|
// Implement the --defsym option on the command line. Return true if
|
||||||
// all is well.
|
// all is well.
|
||||||
|
|
||||||
|
@ -1189,7 +1284,8 @@ Script_options::define_symbol(const char* definition)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manage mapping from keywords to the codes expected by the bison
|
// Manage mapping from keywords to the codes expected by the bison
|
||||||
// parser.
|
// parser. We construct one global object for each lex mode with
|
||||||
|
// keywords.
|
||||||
|
|
||||||
class Keyword_to_parsecode
|
class Keyword_to_parsecode
|
||||||
{
|
{
|
||||||
|
@ -1203,25 +1299,27 @@ class Keyword_to_parsecode
|
||||||
int parsecode;
|
int parsecode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Keyword_to_parsecode(const Keyword_parsecode* keywords,
|
||||||
|
int keyword_count)
|
||||||
|
: keyword_parsecodes_(keywords), keyword_count_(keyword_count)
|
||||||
|
{ }
|
||||||
|
|
||||||
// Return the parsecode corresponding KEYWORD, or 0 if it is not a
|
// Return the parsecode corresponding KEYWORD, or 0 if it is not a
|
||||||
// keyword.
|
// keyword.
|
||||||
static int
|
int
|
||||||
keyword_to_parsecode(const char* keyword, size_t len);
|
keyword_to_parsecode(const char* keyword, size_t len) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// The array of all keywords.
|
const Keyword_parsecode* keyword_parsecodes_;
|
||||||
static const Keyword_parsecode keyword_parsecodes_[];
|
const int keyword_count_;
|
||||||
|
|
||||||
// The number of keywords.
|
|
||||||
static const int keyword_count;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mapping from keyword string to keyword parsecode. This array must
|
// Mapping from keyword string to keyword parsecode. This array must
|
||||||
// be kept in sorted order. Parsecodes are looked up using bsearch.
|
// be kept in sorted order. Parsecodes are looked up using bsearch.
|
||||||
// This array must correspond to the list of parsecodes in yyscript.y.
|
// This array must correspond to the list of parsecodes in yyscript.y.
|
||||||
|
|
||||||
const Keyword_to_parsecode::Keyword_parsecode
|
static const Keyword_to_parsecode::Keyword_parsecode
|
||||||
Keyword_to_parsecode::keyword_parsecodes_[] =
|
script_keyword_parsecodes[] =
|
||||||
{
|
{
|
||||||
{ "ABSOLUTE", ABSOLUTE },
|
{ "ABSOLUTE", ABSOLUTE },
|
||||||
{ "ADDR", ADDR },
|
{ "ADDR", ADDR },
|
||||||
|
@ -1303,9 +1401,23 @@ Keyword_to_parsecode::keyword_parsecodes_[] =
|
||||||
{ "sizeof_headers", SIZEOF_HEADERS },
|
{ "sizeof_headers", SIZEOF_HEADERS },
|
||||||
};
|
};
|
||||||
|
|
||||||
const int Keyword_to_parsecode::keyword_count =
|
static const Keyword_to_parsecode
|
||||||
(sizeof(Keyword_to_parsecode::keyword_parsecodes_)
|
script_keywords(&script_keyword_parsecodes[0],
|
||||||
/ sizeof(Keyword_to_parsecode::keyword_parsecodes_[0]));
|
(sizeof(script_keyword_parsecodes)
|
||||||
|
/ sizeof(script_keyword_parsecodes[0])));
|
||||||
|
|
||||||
|
static const Keyword_to_parsecode::Keyword_parsecode
|
||||||
|
version_script_keyword_parsecodes[] =
|
||||||
|
{
|
||||||
|
{ "extern", EXTERN },
|
||||||
|
{ "global", GLOBAL },
|
||||||
|
{ "local", LOCAL },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const Keyword_to_parsecode
|
||||||
|
version_script_keywords(&version_script_keyword_parsecodes[0],
|
||||||
|
(sizeof(version_script_keyword_parsecodes)
|
||||||
|
/ sizeof(version_script_keyword_parsecodes[0])));
|
||||||
|
|
||||||
// Comparison function passed to bsearch.
|
// Comparison function passed to bsearch.
|
||||||
|
|
||||||
|
@ -1335,16 +1447,17 @@ ktt_compare(const void* keyv, const void* kttv)
|
||||||
} // End extern "C".
|
} // End extern "C".
|
||||||
|
|
||||||
int
|
int
|
||||||
Keyword_to_parsecode::keyword_to_parsecode(const char* keyword, size_t len)
|
Keyword_to_parsecode::keyword_to_parsecode(const char* keyword,
|
||||||
|
size_t len) const
|
||||||
{
|
{
|
||||||
Ktt_key key;
|
Ktt_key key;
|
||||||
key.str = keyword;
|
key.str = keyword;
|
||||||
key.len = len;
|
key.len = len;
|
||||||
void* kttv = bsearch(&key,
|
void* kttv = bsearch(&key,
|
||||||
Keyword_to_parsecode::keyword_parsecodes_,
|
this->keyword_parsecodes_,
|
||||||
Keyword_to_parsecode::keyword_count,
|
this->keyword_count_,
|
||||||
sizeof(Keyword_to_parsecode::keyword_parsecodes_[0]),
|
sizeof(this->keyword_parsecodes_[0]),
|
||||||
ktt_compare);
|
ktt_compare);
|
||||||
if (kttv == NULL)
|
if (kttv == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
Keyword_parsecode* ktt = static_cast<Keyword_parsecode*>(kttv);
|
Keyword_parsecode* ktt = static_cast<Keyword_parsecode*>(kttv);
|
||||||
|
@ -1383,7 +1496,18 @@ yylex(YYSTYPE* lvalp, void* closurev)
|
||||||
// This is either a keyword or a STRING.
|
// This is either a keyword or a STRING.
|
||||||
size_t len;
|
size_t len;
|
||||||
const char* str = token->string_value(&len);
|
const char* str = token->string_value(&len);
|
||||||
int parsecode = Keyword_to_parsecode::keyword_to_parsecode(str, len);
|
int parsecode = 0;
|
||||||
|
switch (closure->lex_mode())
|
||||||
|
{
|
||||||
|
case Lex::LINKER_SCRIPT:
|
||||||
|
parsecode = script_keywords.keyword_to_parsecode(str, len);
|
||||||
|
break;
|
||||||
|
case Lex::VERSION_SCRIPT:
|
||||||
|
parsecode = version_script_keywords.keyword_to_parsecode(str, len);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (parsecode != 0)
|
if (parsecode != 0)
|
||||||
return parsecode;
|
return parsecode;
|
||||||
lvalp->string.value = str;
|
lvalp->string.value = str;
|
||||||
|
@ -1561,6 +1685,16 @@ script_push_lex_into_expression_mode(void* closurev)
|
||||||
closure->push_lex_mode(Lex::EXPRESSION);
|
closure->push_lex_mode(Lex::EXPRESSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called by the bison parser to push the lexer into version
|
||||||
|
mode. */
|
||||||
|
|
||||||
|
extern void
|
||||||
|
script_push_lex_into_version_mode(void* closurev)
|
||||||
|
{
|
||||||
|
Parser_closure* closure = static_cast<Parser_closure*>(closurev);
|
||||||
|
closure->push_lex_mode(Lex::VERSION_SCRIPT);
|
||||||
|
}
|
||||||
|
|
||||||
/* Called by the bison parser to pop the lexer mode. */
|
/* Called by the bison parser to pop the lexer mode. */
|
||||||
|
|
||||||
extern void
|
extern void
|
||||||
|
@ -1569,3 +1703,234 @@ script_pop_lex_mode(void* closurev)
|
||||||
Parser_closure* closure = static_cast<Parser_closure*>(closurev);
|
Parser_closure* closure = static_cast<Parser_closure*>(closurev);
|
||||||
closure->pop_lex_mode();
|
closure->pop_lex_mode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The following structs are used within the VersionInfo class as well
|
||||||
|
// as in the bison helper functions. They store the information
|
||||||
|
// parsed from the version script.
|
||||||
|
|
||||||
|
// A single version expression.
|
||||||
|
// For example, pattern="std::map*" and language="C++".
|
||||||
|
// pattern and language should be from the stringpool
|
||||||
|
struct Version_expression {
|
||||||
|
Version_expression(const std::string& pattern,
|
||||||
|
const std::string& language)
|
||||||
|
: pattern(pattern), language(language) {}
|
||||||
|
|
||||||
|
std::string pattern;
|
||||||
|
std::string language;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// A list of expressions.
|
||||||
|
struct Version_expression_list {
|
||||||
|
std::vector<struct Version_expression> expressions;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// A list of which versions upon which another version depends.
|
||||||
|
// Strings should be from the Stringpool.
|
||||||
|
struct Version_dependency_list {
|
||||||
|
std::vector<std::string> dependencies;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// The total definition of a version. It includes the tag for the
|
||||||
|
// version, its global and local expressions, and any dependencies.
|
||||||
|
struct Version_tree {
|
||||||
|
Version_tree()
|
||||||
|
: tag(), global(NULL), local(NULL), dependencies(NULL) {}
|
||||||
|
|
||||||
|
std::string tag;
|
||||||
|
const struct Version_expression_list* global;
|
||||||
|
const struct Version_expression_list* local;
|
||||||
|
const struct Version_dependency_list* dependencies;
|
||||||
|
};
|
||||||
|
|
||||||
|
Version_script_info::~Version_script_info()
|
||||||
|
{
|
||||||
|
for (size_t k = 0; k < dependency_lists_.size(); ++k)
|
||||||
|
delete dependency_lists_[k];
|
||||||
|
for (size_t k = 0; k < version_trees_.size(); ++k)
|
||||||
|
delete version_trees_[k];
|
||||||
|
for (size_t k = 0; k < expression_lists_.size(); ++k)
|
||||||
|
delete expression_lists_[k];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string>
|
||||||
|
Version_script_info::get_versions() const
|
||||||
|
{
|
||||||
|
std::vector<std::string> ret;
|
||||||
|
for (size_t j = 0; j < version_trees_.size(); ++j)
|
||||||
|
ret.push_back(version_trees_[j]->tag);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string>
|
||||||
|
Version_script_info::get_dependencies(const char* version) const
|
||||||
|
{
|
||||||
|
std::vector<std::string> ret;
|
||||||
|
for (size_t j = 0; j < version_trees_.size(); ++j)
|
||||||
|
if (version_trees_[j]->tag == version)
|
||||||
|
{
|
||||||
|
const struct Version_dependency_list* deps =
|
||||||
|
version_trees_[j]->dependencies;
|
||||||
|
if (deps != NULL)
|
||||||
|
for (size_t k = 0; k < deps->dependencies.size(); ++k)
|
||||||
|
ret.push_back(deps->dependencies[k]);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string&
|
||||||
|
Version_script_info::get_symbol_version_helper(const char* symbol_name,
|
||||||
|
bool check_global) const
|
||||||
|
{
|
||||||
|
for (size_t j = 0; j < version_trees_.size(); ++j)
|
||||||
|
{
|
||||||
|
// Is it a global symbol for this version?
|
||||||
|
const Version_expression_list* exp =
|
||||||
|
check_global ? version_trees_[j]->global : version_trees_[j]->local;
|
||||||
|
if (exp != NULL)
|
||||||
|
for (size_t k = 0; k < exp->expressions.size(); ++k)
|
||||||
|
{
|
||||||
|
const char* name_to_match = symbol_name;
|
||||||
|
char* demangled_name = NULL;
|
||||||
|
if (exp->expressions[k].language == "C++")
|
||||||
|
{
|
||||||
|
demangled_name = cplus_demangle(symbol_name,
|
||||||
|
DMGL_ANSI | DMGL_PARAMS);
|
||||||
|
// This isn't a C++ symbol.
|
||||||
|
if (demangled_name == NULL)
|
||||||
|
continue;
|
||||||
|
name_to_match = demangled_name;
|
||||||
|
}
|
||||||
|
else if (exp->expressions[k].language == "Java")
|
||||||
|
{
|
||||||
|
demangled_name = cplus_demangle(symbol_name,
|
||||||
|
(DMGL_ANSI | DMGL_PARAMS
|
||||||
|
| DMGL_JAVA));
|
||||||
|
// This isn't a Java symbol.
|
||||||
|
if (demangled_name == NULL)
|
||||||
|
continue;
|
||||||
|
name_to_match = demangled_name;
|
||||||
|
}
|
||||||
|
bool matched = fnmatch(exp->expressions[k].pattern.c_str(),
|
||||||
|
name_to_match, FNM_NOESCAPE) == 0;
|
||||||
|
if (demangled_name != NULL)
|
||||||
|
free(demangled_name);
|
||||||
|
if (matched)
|
||||||
|
return version_trees_[j]->tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static const std::string empty = "";
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Version_dependency_list*
|
||||||
|
Version_script_info::allocate_dependency_list()
|
||||||
|
{
|
||||||
|
dependency_lists_.push_back(new Version_dependency_list);
|
||||||
|
return dependency_lists_.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Version_expression_list*
|
||||||
|
Version_script_info::allocate_expression_list()
|
||||||
|
{
|
||||||
|
expression_lists_.push_back(new Version_expression_list);
|
||||||
|
return expression_lists_.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Version_tree*
|
||||||
|
Version_script_info::allocate_version_tree()
|
||||||
|
{
|
||||||
|
version_trees_.push_back(new Version_tree);
|
||||||
|
return version_trees_.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register an entire version node. For example:
|
||||||
|
//
|
||||||
|
// GLIBC_2.1 {
|
||||||
|
// global: foo;
|
||||||
|
// } GLIBC_2.0;
|
||||||
|
//
|
||||||
|
// - tag is "GLIBC_2.1"
|
||||||
|
// - tree contains the information "global: foo"
|
||||||
|
// - deps contains "GLIBC_2.0"
|
||||||
|
|
||||||
|
extern "C" void
|
||||||
|
script_register_vers_node(void*,
|
||||||
|
const char* tag,
|
||||||
|
int taglen,
|
||||||
|
struct Version_tree *tree,
|
||||||
|
struct Version_dependency_list *deps)
|
||||||
|
{
|
||||||
|
gold_assert(tree != NULL);
|
||||||
|
gold_assert(tag != NULL);
|
||||||
|
tree->dependencies = deps;
|
||||||
|
tree->tag = std::string(tag, taglen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a dependencies to the list of existing dependencies, if any,
|
||||||
|
// and return the expanded list.
|
||||||
|
|
||||||
|
extern "C" struct Version_dependency_list *
|
||||||
|
script_add_vers_depend(void* closurev,
|
||||||
|
struct Version_dependency_list *all_deps,
|
||||||
|
const char *depend_to_add, int deplen)
|
||||||
|
{
|
||||||
|
Parser_closure* closure = static_cast<Parser_closure*>(closurev);
|
||||||
|
if (all_deps == NULL)
|
||||||
|
all_deps = closure->version_script()->allocate_dependency_list();
|
||||||
|
all_deps->dependencies.push_back(std::string(depend_to_add, deplen));
|
||||||
|
return all_deps;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a pattern expression to an existing list of expressions, if any.
|
||||||
|
// TODO: In the old linker, the last argument used to be a bool, but I
|
||||||
|
// don't know what it meant.
|
||||||
|
|
||||||
|
extern "C" struct Version_expression_list *
|
||||||
|
script_new_vers_pattern(void* closurev,
|
||||||
|
struct Version_expression_list *expressions,
|
||||||
|
const char *pattern, int patlen)
|
||||||
|
{
|
||||||
|
Parser_closure* closure = static_cast<Parser_closure*>(closurev);
|
||||||
|
if (expressions == NULL)
|
||||||
|
expressions = closure->version_script()->allocate_expression_list();
|
||||||
|
expressions->expressions.push_back(
|
||||||
|
Version_expression(std::string(pattern, patlen),
|
||||||
|
closure->get_current_language()));
|
||||||
|
return expressions;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine the global and local expressions into a a Version_tree.
|
||||||
|
|
||||||
|
extern "C" struct Version_tree *
|
||||||
|
script_new_vers_node(void* closurev,
|
||||||
|
struct Version_expression_list *global,
|
||||||
|
struct Version_expression_list *local)
|
||||||
|
{
|
||||||
|
Parser_closure* closure = static_cast<Parser_closure*>(closurev);
|
||||||
|
Version_tree* tree = closure->version_script()->allocate_version_tree();
|
||||||
|
tree->global = global;
|
||||||
|
tree->local = local;
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle a transition in language, such as at the
|
||||||
|
// start or end of 'extern "C++"'
|
||||||
|
|
||||||
|
extern "C" void
|
||||||
|
version_script_push_lang(void* closurev, const char* lang, int langlen)
|
||||||
|
{
|
||||||
|
Parser_closure* closure = static_cast<Parser_closure*>(closurev);
|
||||||
|
closure->push_language(std::string(lang, langlen));
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void
|
||||||
|
version_script_pop_lang(void* closurev)
|
||||||
|
{
|
||||||
|
Parser_closure* closure = static_cast<Parser_closure*>(closurev);
|
||||||
|
closure->pop_language();
|
||||||
|
}
|
||||||
|
|
|
@ -32,6 +32,10 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
struct Version_dependency_list;
|
||||||
|
struct Version_expression_list;
|
||||||
|
struct Version_tree;
|
||||||
|
|
||||||
namespace gold
|
namespace gold
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -80,6 +84,69 @@ class Expression
|
||||||
Expression& operator=(const Expression&);
|
Expression& operator=(const Expression&);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Version_script_info stores information parsed from the version
|
||||||
|
// script, either provided by --version-script or as part of a linker
|
||||||
|
// script. A single Version_script_info object per target is owned by
|
||||||
|
// Script_options.
|
||||||
|
|
||||||
|
class Version_script_info {
|
||||||
|
public:
|
||||||
|
~Version_script_info();
|
||||||
|
|
||||||
|
// Return whether any version were defined in the version script.
|
||||||
|
bool
|
||||||
|
empty() const
|
||||||
|
{ return this->version_trees_.empty(); }
|
||||||
|
|
||||||
|
// Return the version associated with the given symbol name.
|
||||||
|
// Strings are allocated out of the stringpool given in the
|
||||||
|
// constructor. Strings are allocated out of the stringpool given
|
||||||
|
// in the constructor.
|
||||||
|
const std::string&
|
||||||
|
get_symbol_version(const char* symbol) const
|
||||||
|
{ return get_symbol_version_helper(symbol, true); }
|
||||||
|
|
||||||
|
// Return whether this symbol matches the local: section of a
|
||||||
|
// version script (it doesn't matter which). This test is only
|
||||||
|
// valid if get_symbol_version() returns the empty string, as we
|
||||||
|
// don't test that here.
|
||||||
|
bool
|
||||||
|
symbol_is_local(const char* symbol) const
|
||||||
|
{ return !get_symbol_version_helper(symbol, false).empty(); }
|
||||||
|
|
||||||
|
// Return the names of versions defined in the version script.
|
||||||
|
// Strings are allocated out of the stringpool given in the
|
||||||
|
// constructor.
|
||||||
|
std::vector<std::string>
|
||||||
|
get_versions() const;
|
||||||
|
|
||||||
|
// Return the list of dependencies for this version.
|
||||||
|
std::vector<std::string>
|
||||||
|
get_dependencies(const char* version) const;
|
||||||
|
|
||||||
|
// The following functions should only be used by the bison helper
|
||||||
|
// functions. They allocate new structs whose memory belongs to
|
||||||
|
// Version_script_info. The bison functions copy the information
|
||||||
|
// from the version script into these structs.
|
||||||
|
struct Version_dependency_list*
|
||||||
|
allocate_dependency_list();
|
||||||
|
|
||||||
|
struct Version_expression_list*
|
||||||
|
allocate_expression_list();
|
||||||
|
|
||||||
|
struct Version_tree*
|
||||||
|
allocate_version_tree();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string& get_symbol_version_helper(const char* symbol,
|
||||||
|
bool check_global) const;
|
||||||
|
|
||||||
|
std::vector<struct Version_dependency_list*> dependency_lists_;
|
||||||
|
std::vector<struct Version_expression_list*> expression_lists_;
|
||||||
|
std::vector<struct Version_tree*> version_trees_;
|
||||||
|
};
|
||||||
|
|
||||||
// We can read a linker script in two different contexts: when
|
// We can read a linker script in two different contexts: when
|
||||||
// initially parsing the command line, and when we find an input file
|
// initially parsing the command line, and when we find an input file
|
||||||
// which is actually a linker script. Also some of the data which can
|
// which is actually a linker script. Also some of the data which can
|
||||||
|
@ -127,6 +194,12 @@ class Script_options
|
||||||
void
|
void
|
||||||
finalize_symbols(Symbol_table*, const Layout*);
|
finalize_symbols(Symbol_table*, const Layout*);
|
||||||
|
|
||||||
|
// Version information parsed from a version script. Everything
|
||||||
|
// else has a pointer to this object.
|
||||||
|
Version_script_info*
|
||||||
|
version_script_info()
|
||||||
|
{ return &version_script_info_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// We keep a list of symbol assignments.
|
// We keep a list of symbol assignments.
|
||||||
struct Symbol_assignment
|
struct Symbol_assignment
|
||||||
|
@ -160,6 +233,8 @@ class Script_options
|
||||||
std::string entry_;
|
std::string entry_;
|
||||||
// Symbols to set.
|
// Symbols to set.
|
||||||
Symbol_assignments symbol_assignments_;
|
Symbol_assignments symbol_assignments_;
|
||||||
|
// Version information parsed from a version script.
|
||||||
|
Version_script_info version_script_info_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// FILE was found as an argument on the command line, but was not
|
// FILE was found as an argument on the command line, but was not
|
||||||
|
@ -181,6 +256,14 @@ read_input_script(Workqueue*, const General_options&, Symbol_table*, Layout*,
|
||||||
bool
|
bool
|
||||||
read_commandline_script(const char* filename, Command_line*);
|
read_commandline_script(const char* filename, Command_line*);
|
||||||
|
|
||||||
|
// FILE was found as an argument to --version-script. Read it as a
|
||||||
|
// version script, and store its contents in
|
||||||
|
// cmdline->script_options()->version_script_info().
|
||||||
|
|
||||||
|
bool
|
||||||
|
read_version_script(const char* filename, Command_line* cmdline);
|
||||||
|
|
||||||
|
|
||||||
} // End namespace gold.
|
} // End namespace gold.
|
||||||
|
|
||||||
#endif // !defined(GOLD_SCRIPT_H)
|
#endif // !defined(GOLD_SCRIPT_H)
|
||||||
|
|
|
@ -295,9 +295,10 @@ Symbol::final_value_is_known() const
|
||||||
|
|
||||||
// Class Symbol_table.
|
// Class Symbol_table.
|
||||||
|
|
||||||
Symbol_table::Symbol_table(unsigned int count)
|
Symbol_table::Symbol_table(unsigned int count,
|
||||||
|
const Version_script_info& version_script)
|
||||||
: saw_undefined_(0), offset_(0), table_(count), namepool_(),
|
: saw_undefined_(0), offset_(0), table_(count), namepool_(),
|
||||||
forwarders_(), commons_(), warnings_()
|
forwarders_(), commons_(), warnings_(), version_script_(version_script)
|
||||||
{
|
{
|
||||||
namepool_.reserve(count);
|
namepool_.reserve(count);
|
||||||
}
|
}
|
||||||
|
@ -571,6 +572,7 @@ Symbol_table::add_from_object(Object* object,
|
||||||
if (!was_common && ret->is_common())
|
if (!was_common && ret->is_common())
|
||||||
this->commons_.push_back(ret);
|
this->commons_.push_back(ret);
|
||||||
|
|
||||||
|
ret->set_is_default(def);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -626,6 +628,31 @@ Symbol_table::add_from_relobj(
|
||||||
// name from the version name. If there are two '@' characters,
|
// name from the version name. If there are two '@' characters,
|
||||||
// this is the default version.
|
// this is the default version.
|
||||||
const char* ver = strchr(name, '@');
|
const char* ver = strchr(name, '@');
|
||||||
|
int namelen = 0;
|
||||||
|
bool def = false;
|
||||||
|
|
||||||
|
if (ver != NULL)
|
||||||
|
{
|
||||||
|
// The symbol name is of the form foo@VERSION or foo@@VERSION
|
||||||
|
namelen = ver - name;
|
||||||
|
++ver;
|
||||||
|
if (*ver == '@')
|
||||||
|
{
|
||||||
|
def = true;
|
||||||
|
++ver;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!version_script_.empty())
|
||||||
|
{
|
||||||
|
// The symbol name did not have a version, but
|
||||||
|
// the version script may assign a version anyway.
|
||||||
|
namelen = strlen(name);
|
||||||
|
def = true;
|
||||||
|
const std::string& version =
|
||||||
|
version_script_.get_symbol_version(name);
|
||||||
|
if (!version.empty())
|
||||||
|
ver = version.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
Sized_symbol<size>* res;
|
Sized_symbol<size>* res;
|
||||||
if (ver == NULL)
|
if (ver == NULL)
|
||||||
|
@ -638,17 +665,8 @@ Symbol_table::add_from_relobj(
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Stringpool::Key name_key;
|
Stringpool::Key name_key;
|
||||||
name = this->namepool_.add_with_length(name, ver - name, true,
|
name = this->namepool_.add_with_length(name, namelen, true,
|
||||||
&name_key);
|
&name_key);
|
||||||
|
|
||||||
bool def = false;
|
|
||||||
++ver;
|
|
||||||
if (*ver == '@')
|
|
||||||
{
|
|
||||||
def = true;
|
|
||||||
++ver;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stringpool::Key ver_key;
|
Stringpool::Key ver_key;
|
||||||
ver = this->namepool_.add(ver, true, &ver_key);
|
ver = this->namepool_.add(ver, true, &ver_key);
|
||||||
|
|
||||||
|
@ -1238,7 +1256,7 @@ Symbol_table::do_define_as_constant(
|
||||||
if (sym == NULL)
|
if (sym == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
gold_assert(version == NULL || oldsym != NULL);
|
gold_assert(version == NULL || version == name || oldsym != NULL);
|
||||||
sym->init(name, value, symsize, type, binding, visibility, nonvis);
|
sym->init(name, value, symsize, type, binding, visibility, nonvis);
|
||||||
|
|
||||||
if (oldsym == NULL)
|
if (oldsym == NULL)
|
||||||
|
@ -1392,8 +1410,8 @@ Symbol_table::set_dynsym_indexes(const Target* target,
|
||||||
dynpool->add(sym->name(), false, NULL);
|
dynpool->add(sym->name(), false, NULL);
|
||||||
|
|
||||||
// Record any version information.
|
// Record any version information.
|
||||||
if (sym->version() != NULL)
|
if (sym->version() != NULL)
|
||||||
versions->record_version(this, dynpool, sym);
|
versions->record_version(this, dynpool, sym);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ class Dynobj;
|
||||||
template<int size, bool big_endian>
|
template<int size, bool big_endian>
|
||||||
class Sized_dynobj;
|
class Sized_dynobj;
|
||||||
class Versions;
|
class Versions;
|
||||||
|
class Version_script_info;
|
||||||
class Input_objects;
|
class Input_objects;
|
||||||
class Output_data;
|
class Output_data;
|
||||||
class Output_section;
|
class Output_section;
|
||||||
|
@ -111,6 +112,21 @@ class Symbol
|
||||||
version() const
|
version() const
|
||||||
{ return this->version_; }
|
{ return this->version_; }
|
||||||
|
|
||||||
|
// Return whether this version is the default for this symbol name
|
||||||
|
// (eg, "foo@@V2" is a default version; "foo@V1" is not). Only
|
||||||
|
// meaningful for versioned symbols.
|
||||||
|
bool
|
||||||
|
is_default() const
|
||||||
|
{
|
||||||
|
gold_assert(this->version_ != NULL);
|
||||||
|
return this->is_def_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set whether this version is the default for this symbol name.
|
||||||
|
void
|
||||||
|
set_is_default(bool def)
|
||||||
|
{ this->is_def_ = def; }
|
||||||
|
|
||||||
// Return the symbol source.
|
// Return the symbol source.
|
||||||
Source
|
Source
|
||||||
source() const
|
source() const
|
||||||
|
@ -953,7 +969,7 @@ class Symbol_table
|
||||||
// COUNT is an estimate of how many symbosl will be inserted in the
|
// COUNT is an estimate of how many symbosl will be inserted in the
|
||||||
// symbol table. It's ok to put 0 if you don't know; a correct
|
// symbol table. It's ok to put 0 if you don't know; a correct
|
||||||
// guess will just save some CPU by reducing hashtable resizes.
|
// guess will just save some CPU by reducing hashtable resizes.
|
||||||
Symbol_table(unsigned int count);
|
Symbol_table(unsigned int count, const Version_script_info& version_script);
|
||||||
|
|
||||||
~Symbol_table();
|
~Symbol_table();
|
||||||
|
|
||||||
|
@ -1115,6 +1131,11 @@ class Symbol_table
|
||||||
void
|
void
|
||||||
print_stats() const;
|
print_stats() const;
|
||||||
|
|
||||||
|
// Return the version script information.
|
||||||
|
const Version_script_info&
|
||||||
|
version_script() const
|
||||||
|
{ return version_script_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Symbol_table(const Symbol_table&);
|
Symbol_table(const Symbol_table&);
|
||||||
Symbol_table& operator=(const Symbol_table&);
|
Symbol_table& operator=(const Symbol_table&);
|
||||||
|
@ -1347,6 +1368,8 @@ class Symbol_table
|
||||||
// definition. This maps symbols with COPY relocs to the dynamic
|
// definition. This maps symbols with COPY relocs to the dynamic
|
||||||
// object where they were defined.
|
// object where they were defined.
|
||||||
Copied_symbol_dynobjs copied_symbol_dynobjs_;
|
Copied_symbol_dynobjs copied_symbol_dynobjs_;
|
||||||
|
// Information parsed from the version script, if any.
|
||||||
|
const Version_script_info& version_script_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// We inline get_sized_symbol for efficiency.
|
// We inline get_sized_symbol for efficiency.
|
||||||
|
|
|
@ -490,10 +490,10 @@ ver_test_LDFLAGS = -Bgcctestdir/ -Wl,-R,.
|
||||||
ver_test_LDADD = ver_test_1.so ver_test_2.so ver_test_4.so
|
ver_test_LDADD = ver_test_1.so ver_test_2.so ver_test_4.so
|
||||||
ver_test_1.so: ver_test_1.o ver_test_2.so ver_test_3.o ver_test_4.so gcctestdir/ld
|
ver_test_1.so: ver_test_1.o ver_test_2.so ver_test_3.o ver_test_4.so gcctestdir/ld
|
||||||
$(CXXLINK) -Bgcctestdir/ -shared ver_test_1.o ver_test_2.so ver_test_3.o ver_test_4.so
|
$(CXXLINK) -Bgcctestdir/ -shared ver_test_1.o ver_test_2.so ver_test_3.o ver_test_4.so
|
||||||
ver_test_2.so: ver_test_2.o $(srcdir)/ver_test_2.script ver_test_4.so
|
ver_test_2.so: ver_test_2.o $(srcdir)/ver_test_2.script ver_test_4.so gcctestdir/ld
|
||||||
$(CXXLINK) -shared -Wl,--version-script,$(srcdir)/ver_test_2.script ver_test_2.o ver_test_4.so
|
$(CXXLINK) -Bgcctestdir/ -shared -Wl,--version-script,$(srcdir)/ver_test_2.script ver_test_2.o ver_test_4.so
|
||||||
ver_test_4.so: ver_test_4.o $(srcdir)/ver_test_4.script
|
ver_test_4.so: ver_test_4.o $(srcdir)/ver_test_4.script gcctestdir/ld
|
||||||
$(CXXLINK) -shared -Wl,--version-script,$(srcdir)/ver_test_4.script ver_test_4.o
|
$(CXXLINK) -Bgcctestdir/ -shared -Wl,--version-script,$(srcdir)/ver_test_4.script ver_test_4.o
|
||||||
ver_test_1.o: ver_test_1.cc
|
ver_test_1.o: ver_test_1.cc
|
||||||
$(CXXCOMPILE) -c -fpic -o $@ $<
|
$(CXXCOMPILE) -c -fpic -o $@ $<
|
||||||
ver_test_2.o: ver_test_2.cc
|
ver_test_2.o: ver_test_2.cc
|
||||||
|
@ -508,5 +508,15 @@ script_test_1_SOURCES = script_test_1.cc
|
||||||
script_test_1_DEPENDENCIES = gcctestdir/ld script_test_1.t
|
script_test_1_DEPENDENCIES = gcctestdir/ld script_test_1.t
|
||||||
script_test_1_LDFLAGS = -Bgcctestdir/ -Wl,-R,. -T $(srcdir)/script_test_1.t
|
script_test_1_LDFLAGS = -Bgcctestdir/ -Wl,-R,. -T $(srcdir)/script_test_1.t
|
||||||
|
|
||||||
|
if OBJDUMP_AND_CPPFILT
|
||||||
|
check_SCRIPTS += ver_matching_test.sh
|
||||||
|
check_DATA += ver_matching_test.stdout
|
||||||
|
MOSTLYCLEANFILES += ver_matching_test.stdout
|
||||||
|
ver_matching_def.so: ver_matching_def.cc gcctestdir/ld
|
||||||
|
$(CXXLINK) -O0 -Bgcctestdir/ -shared $(srcdir)/ver_matching_def.cc -Wl,--version-script=$(srcdir)/version_script.map
|
||||||
|
ver_matching_test.stdout: ver_matching_def.so
|
||||||
|
objdump -T ver_matching_def.so | c++filt > ver_matching_test.stdout
|
||||||
|
endif OBJDUMP_AND_CPPFILT
|
||||||
|
|
||||||
endif GCC
|
endif GCC
|
||||||
endif NATIVE_LINKER
|
endif NATIVE_LINKER
|
||||||
|
|
|
@ -179,6 +179,9 @@ check_PROGRAMS = object_unittest$(EXEEXT) $(am__EXEEXT_1) \
|
||||||
@NATIVE_LINKER_FALSE@ ../libgold.a ../../libiberty/libiberty.a \
|
@NATIVE_LINKER_FALSE@ ../libgold.a ../../libiberty/libiberty.a \
|
||||||
@NATIVE_LINKER_FALSE@ $(am__DEPENDENCIES_1) \
|
@NATIVE_LINKER_FALSE@ $(am__DEPENDENCIES_1) \
|
||||||
@NATIVE_LINKER_FALSE@ $(am__DEPENDENCIES_1)
|
@NATIVE_LINKER_FALSE@ $(am__DEPENDENCIES_1)
|
||||||
|
@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__append_11 = ver_matching_test.sh
|
||||||
|
@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__append_12 = ver_matching_test.stdout
|
||||||
|
@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__append_13 = ver_matching_test.stdout
|
||||||
subdir = testsuite
|
subdir = testsuite
|
||||||
DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
|
DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
|
||||||
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
||||||
|
@ -633,6 +636,8 @@ MSGMERGE = @MSGMERGE@
|
||||||
NATIVE_LINKER_FALSE = @NATIVE_LINKER_FALSE@
|
NATIVE_LINKER_FALSE = @NATIVE_LINKER_FALSE@
|
||||||
NATIVE_LINKER_TRUE = @NATIVE_LINKER_TRUE@
|
NATIVE_LINKER_TRUE = @NATIVE_LINKER_TRUE@
|
||||||
NO_WERROR = @NO_WERROR@
|
NO_WERROR = @NO_WERROR@
|
||||||
|
OBJDUMP_AND_CPPFILT_FALSE = @OBJDUMP_AND_CPPFILT_FALSE@
|
||||||
|
OBJDUMP_AND_CPPFILT_TRUE = @OBJDUMP_AND_CPPFILT_TRUE@
|
||||||
OBJEXT = @OBJEXT@
|
OBJEXT = @OBJEXT@
|
||||||
PACKAGE = @PACKAGE@
|
PACKAGE = @PACKAGE@
|
||||||
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
|
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
|
||||||
|
@ -722,13 +727,13 @@ INCLUDES = \
|
||||||
# .o's), but not all of them (such as .so's and .err files). We
|
# .o's), but not all of them (such as .so's and .err files). We
|
||||||
# improve on that here. automake-1.9 info docs say "mostlyclean" is
|
# improve on that here. automake-1.9 info docs say "mostlyclean" is
|
||||||
# the right choice for files 'make' builds that people rebuild.
|
# the right choice for files 'make' builds that people rebuild.
|
||||||
MOSTLYCLEANFILES = *.so $(am__append_9)
|
MOSTLYCLEANFILES = *.so $(am__append_9) $(am__append_13)
|
||||||
|
|
||||||
# We will add to these later, for each individual test. Note
|
# We will add to these later, for each individual test. Note
|
||||||
# that we add each test under check_SCRIPTS or check_PROGRAMS;
|
# that we add each test under check_SCRIPTS or check_PROGRAMS;
|
||||||
# the TESTS variable is automatically populated from these.
|
# the TESTS variable is automatically populated from these.
|
||||||
check_SCRIPTS = $(am__append_7)
|
check_SCRIPTS = $(am__append_7) $(am__append_11)
|
||||||
check_DATA = $(am__append_8)
|
check_DATA = $(am__append_8) $(am__append_12)
|
||||||
TESTS = $(check_SCRIPTS) $(check_PROGRAMS)
|
TESTS = $(check_SCRIPTS) $(check_PROGRAMS)
|
||||||
|
|
||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
|
@ -1561,10 +1566,10 @@ uninstall-am: uninstall-info-am
|
||||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ test -s $@
|
@GCC_TRUE@@NATIVE_LINKER_TRUE@ test -s $@
|
||||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_1.so: ver_test_1.o ver_test_2.so ver_test_3.o ver_test_4.so gcctestdir/ld
|
@GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_1.so: ver_test_1.o ver_test_2.so ver_test_3.o ver_test_4.so gcctestdir/ld
|
||||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -shared ver_test_1.o ver_test_2.so ver_test_3.o ver_test_4.so
|
@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -shared ver_test_1.o ver_test_2.so ver_test_3.o ver_test_4.so
|
||||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_2.so: ver_test_2.o $(srcdir)/ver_test_2.script ver_test_4.so
|
@GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_2.so: ver_test_2.o $(srcdir)/ver_test_2.script ver_test_4.so gcctestdir/ld
|
||||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -shared -Wl,--version-script,$(srcdir)/ver_test_2.script ver_test_2.o ver_test_4.so
|
@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -shared -Wl,--version-script,$(srcdir)/ver_test_2.script ver_test_2.o ver_test_4.so
|
||||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_4.so: ver_test_4.o $(srcdir)/ver_test_4.script
|
@GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_4.so: ver_test_4.o $(srcdir)/ver_test_4.script gcctestdir/ld
|
||||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -shared -Wl,--version-script,$(srcdir)/ver_test_4.script ver_test_4.o
|
@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -shared -Wl,--version-script,$(srcdir)/ver_test_4.script ver_test_4.o
|
||||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_1.o: ver_test_1.cc
|
@GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_1.o: ver_test_1.cc
|
||||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -c -fpic -o $@ $<
|
@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -c -fpic -o $@ $<
|
||||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_2.o: ver_test_2.cc
|
@GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_2.o: ver_test_2.cc
|
||||||
|
@ -1573,6 +1578,10 @@ uninstall-am: uninstall-info-am
|
||||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -c -fpic -o $@ $<
|
@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -c -fpic -o $@ $<
|
||||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_4.o: ver_test_4.cc
|
@GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_4.o: ver_test_4.cc
|
||||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -c -fpic -o $@ $<
|
@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -c -fpic -o $@ $<
|
||||||
|
@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@ver_matching_def.so: ver_matching_def.cc gcctestdir/ld
|
||||||
|
@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@ $(CXXLINK) -O0 -Bgcctestdir/ -shared $(srcdir)/ver_matching_def.cc -Wl,--version-script=$(srcdir)/version_script.map
|
||||||
|
@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@ver_matching_test.stdout: ver_matching_def.so
|
||||||
|
@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@ objdump -T ver_matching_def.so | c++filt > ver_matching_test.stdout
|
||||||
# Tell versions [3.59,3.63) of GNU make to not export all variables.
|
# Tell versions [3.59,3.63) of GNU make to not export all variables.
|
||||||
# Otherwise a system limit (for SysV at least) may be exceeded.
|
# Otherwise a system limit (for SysV at least) may be exceeded.
|
||||||
.NOEXPORT:
|
.NOEXPORT:
|
||||||
|
|
69
gold/testsuite/ver_matching_def.cc
Normal file
69
gold/testsuite/ver_matching_def.cc
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
// ver_matching_def.cc - test matching rules in version_script.map
|
||||||
|
|
||||||
|
// Copyright 2007 Free Software Foundation, Inc.
|
||||||
|
// Written by Cary Coutant <ccoutant@google.com>.
|
||||||
|
|
||||||
|
// This file is part of gold.
|
||||||
|
|
||||||
|
// This program is free software; you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation; either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301, USA.
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
void foo() {} // V1
|
||||||
|
void foo1() {} // local
|
||||||
|
};
|
||||||
|
|
||||||
|
void bar() {} // V1
|
||||||
|
void bar1() {} // global
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
void bar2() {} // V1
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace myns {
|
||||||
|
void blah() {} // V1
|
||||||
|
void bip() {} // V1
|
||||||
|
|
||||||
|
class Stuff {
|
||||||
|
public:
|
||||||
|
Stuff() {} // V1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class Biz {
|
||||||
|
public:
|
||||||
|
Biz() {} // global
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace otherns {
|
||||||
|
Biz biz; // global
|
||||||
|
myns::Stuff stuff; // V2
|
||||||
|
};
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
void blaza() {} // V1
|
||||||
|
void blaza1() {} // V1
|
||||||
|
|
||||||
|
void original_blaza2() {} // V2
|
||||||
|
__asm__(".symver original_blaza2,blaza2@@V2"); // overrides script
|
||||||
|
|
||||||
|
void bla() {} // global
|
||||||
|
void blaz() {} // V2
|
||||||
|
void blazb() {} // V2
|
||||||
|
|
||||||
|
int globaoeufxstuff = 0; // V1
|
||||||
|
int globaoeufostuff = 0; // global
|
||||||
|
float sizeof_headers = 50.0; // V1
|
||||||
|
};
|
80
gold/testsuite/ver_matching_test.sh
Executable file
80
gold/testsuite/ver_matching_test.sh
Executable file
|
@ -0,0 +1,80 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# ver_matching_test.sh -- a test case for version script matching
|
||||||
|
|
||||||
|
# Copyright 2008 Free Software Foundation, Inc.
|
||||||
|
# Written by Ian Lance Taylor <iant@google.com>.
|
||||||
|
|
||||||
|
# This file is part of gold.
|
||||||
|
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
||||||
|
# MA 02110-1301, USA.
|
||||||
|
|
||||||
|
# This file goes with ver_matching_def.cc, a C++ source file
|
||||||
|
# constructed with several symbols mapped via version_script.map. We
|
||||||
|
# run readelf on the resulting shared object and check that each
|
||||||
|
# symbol has the correct version.
|
||||||
|
|
||||||
|
check()
|
||||||
|
{
|
||||||
|
if ! grep -q "$2" "$1"
|
||||||
|
then
|
||||||
|
echo "Did not find expected symbol in $1:"
|
||||||
|
echo " $2"
|
||||||
|
echo ""
|
||||||
|
echo "Actual output below:"
|
||||||
|
cat "$1"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_missing()
|
||||||
|
{
|
||||||
|
if grep -q "$2" "$1"
|
||||||
|
then
|
||||||
|
echo "Found unexpected symbol in $1:"
|
||||||
|
echo " $2"
|
||||||
|
echo ""
|
||||||
|
echo "Actual output below:"
|
||||||
|
cat "$1"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check ver_matching_test.stdout "V1 *sizeof_headers$"
|
||||||
|
check ver_matching_test.stdout "Base *globaoeufostuff$"
|
||||||
|
check ver_matching_test.stdout "V1 *globaoeufxstuff$"
|
||||||
|
check ver_matching_test.stdout "V2 *otherns::stuff$"
|
||||||
|
check ver_matching_test.stdout "Base *otherns::biz$"
|
||||||
|
check ver_matching_test.stdout "V1 *foo$"
|
||||||
|
check ver_matching_test.stdout "V1 *bar()$"
|
||||||
|
check ver_matching_test.stdout "Base *bar1()$"
|
||||||
|
check ver_matching_test.stdout "V1 *bar2$"
|
||||||
|
check ver_matching_test.stdout "V1 *myns::blah()$"
|
||||||
|
check ver_matching_test.stdout "V1 *myns::bip()$"
|
||||||
|
check ver_matching_test.stdout "V1 *myns::Stuff::Stuff()$"
|
||||||
|
check ver_matching_test.stdout "Base *Biz::Biz()$"
|
||||||
|
check ver_matching_test.stdout "V1 *blaza1$"
|
||||||
|
check ver_matching_test.stdout "V2 *blaza2$"
|
||||||
|
check ver_matching_test.stdout "V1 *blaza$"
|
||||||
|
check ver_matching_test.stdout "Base *bla$"
|
||||||
|
check ver_matching_test.stdout "V2 *blaz$"
|
||||||
|
check ver_matching_test.stdout "V2 *blazb$"
|
||||||
|
|
||||||
|
# TODO: foo1 should be a local symbol and not show up in the .dynsym
|
||||||
|
# dump, but we haven't figured out how to suppress it yet.
|
||||||
|
# check_missing ver_matching_test.stdout "foo1"
|
||||||
|
|
||||||
|
exit 0
|
28
gold/testsuite/version_script.map
Normal file
28
gold/testsuite/version_script.map
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
V1 {
|
||||||
|
global:
|
||||||
|
extern "C++"
|
||||||
|
{
|
||||||
|
"bar()";
|
||||||
|
myns::*;
|
||||||
|
};
|
||||||
|
foo;
|
||||||
|
blaza*;
|
||||||
|
bar*;
|
||||||
|
# Would be a keyword in a linker script.
|
||||||
|
SECTIONS;
|
||||||
|
sizeof_headers;
|
||||||
|
# Crazy globbiness
|
||||||
|
glob*f[^A-Zo]stuff;
|
||||||
|
|
||||||
|
local:
|
||||||
|
*foo*;
|
||||||
|
};
|
||||||
|
|
||||||
|
V2 {
|
||||||
|
global:
|
||||||
|
extern "C++" {
|
||||||
|
otherns::stuff;
|
||||||
|
};
|
||||||
|
blaz*;
|
||||||
|
foo;
|
||||||
|
} V1;
|
|
@ -55,6 +55,10 @@
|
||||||
uint64_t integer;
|
uint64_t integer;
|
||||||
/* An expression. */
|
/* An expression. */
|
||||||
Expression_ptr expr;
|
Expression_ptr expr;
|
||||||
|
// Used for version scripts and within VERSION {}
|
||||||
|
struct Version_dependency_list* deplist;
|
||||||
|
struct Version_expression_list* versyms;
|
||||||
|
struct Version_tree* versnode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Operators, including a precedence table for expressions. */
|
/* Operators, including a precedence table for expressions. */
|
||||||
|
@ -178,6 +182,9 @@
|
||||||
/* Non-terminal types, where needed. */
|
/* Non-terminal types, where needed. */
|
||||||
|
|
||||||
%type <expr> parse_exp exp
|
%type <expr> parse_exp exp
|
||||||
|
%type <versyms> vers_defns
|
||||||
|
%type <versnode> vers_tag
|
||||||
|
%type <deplist> verdep
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
|
@ -202,6 +209,10 @@ file_cmd:
|
||||||
{ script_end_group(closure); }
|
{ script_end_group(closure); }
|
||||||
| OPTION '(' STRING ')'
|
| OPTION '(' STRING ')'
|
||||||
{ script_parse_option(closure, $3.value, $3.length); }
|
{ script_parse_option(closure, $3.value, $3.length); }
|
||||||
|
| VERSIONK '{'
|
||||||
|
{ script_push_lex_into_version_mode(closure); }
|
||||||
|
version_script '}'
|
||||||
|
{ script_pop_lex_mode(closure); }
|
||||||
| file_or_sections_cmd
|
| file_or_sections_cmd
|
||||||
| ignore_cmd
|
| ignore_cmd
|
||||||
;
|
;
|
||||||
|
@ -412,8 +423,84 @@ defsym_expr:
|
||||||
{ script_set_symbol(closure, $1.value, $1.length, $3, 0, 0); }
|
{ script_set_symbol(closure, $1.value, $1.length, $3, 0, 0); }
|
||||||
;
|
;
|
||||||
|
|
||||||
/* A version script. Not yet implemented. */
|
/* A version script. */
|
||||||
version_script:
|
version_script:
|
||||||
|
vers_nodes
|
||||||
|
;
|
||||||
|
|
||||||
|
vers_nodes:
|
||||||
|
vers_node
|
||||||
|
| vers_nodes vers_node
|
||||||
|
;
|
||||||
|
|
||||||
|
vers_node:
|
||||||
|
'{' vers_tag '}' ';'
|
||||||
|
{
|
||||||
|
script_register_vers_node (closure, NULL, 0, $2, NULL);
|
||||||
|
}
|
||||||
|
| STRING '{' vers_tag '}' ';'
|
||||||
|
{
|
||||||
|
script_register_vers_node (closure, $1.value, $1.length, $3,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
| STRING '{' vers_tag '}' verdep ';'
|
||||||
|
{
|
||||||
|
script_register_vers_node (closure, $1.value, $1.length, $3, $5);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
verdep:
|
||||||
|
STRING
|
||||||
|
{
|
||||||
|
$$ = script_add_vers_depend (closure, NULL, $1.value, $1.length);
|
||||||
|
}
|
||||||
|
| verdep STRING
|
||||||
|
{
|
||||||
|
$$ = script_add_vers_depend (closure, $1, $2.value, $2.length);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
vers_tag:
|
||||||
|
/* empty */
|
||||||
|
{ $$ = script_new_vers_node (closure, NULL, NULL); }
|
||||||
|
| vers_defns ';'
|
||||||
|
{ $$ = script_new_vers_node (closure, $1, NULL); }
|
||||||
|
| GLOBAL ':' vers_defns ';'
|
||||||
|
{ $$ = script_new_vers_node (closure, $3, NULL); }
|
||||||
|
| LOCAL ':' vers_defns ';'
|
||||||
|
{ $$ = script_new_vers_node (closure, NULL, $3); }
|
||||||
|
| GLOBAL ':' vers_defns ';' LOCAL ':' vers_defns ';'
|
||||||
|
{ $$ = script_new_vers_node (closure, $3, $7); }
|
||||||
|
;
|
||||||
|
|
||||||
|
vers_defns:
|
||||||
|
STRING
|
||||||
|
{
|
||||||
|
$$ = script_new_vers_pattern (closure, NULL, $1.value,
|
||||||
|
$1.length);
|
||||||
|
}
|
||||||
|
| vers_defns ';' STRING
|
||||||
|
{
|
||||||
|
$$ = script_new_vers_pattern (closure, $1, $3.value, $3.length);
|
||||||
|
}
|
||||||
|
| /* Push STRING on the language stack. */
|
||||||
|
EXTERN STRING '{'
|
||||||
|
{ version_script_push_lang(closure, $2.value, $2.length); }
|
||||||
|
vers_defns opt_semicolon '}'
|
||||||
|
{
|
||||||
|
$$ = $5;
|
||||||
|
version_script_pop_lang(closure);
|
||||||
|
}
|
||||||
|
| EXTERN // "extern" as a symbol name
|
||||||
|
{
|
||||||
|
$$ = script_new_vers_pattern (closure, NULL, "extern",
|
||||||
|
sizeof("extern") - 1);
|
||||||
|
}
|
||||||
|
| vers_defns ';' EXTERN
|
||||||
|
{
|
||||||
|
$$ = script_new_vers_pattern (closure, $1, "extern",
|
||||||
|
sizeof("extern") - 1);
|
||||||
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
/* Some statements require a terminator, which may be a semicolon or a
|
/* Some statements require a terminator, which may be a semicolon or a
|
||||||
|
@ -423,6 +510,12 @@ end:
|
||||||
| ','
|
| ','
|
||||||
;
|
;
|
||||||
|
|
||||||
|
/* An optional semicolon. */
|
||||||
|
opt_semicolon:
|
||||||
|
';'
|
||||||
|
| /* empty */
|
||||||
|
;
|
||||||
|
|
||||||
/* An optional comma. */
|
/* An optional comma. */
|
||||||
opt_comma:
|
opt_comma:
|
||||||
','
|
','
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue