summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gold/dynobj.cc95
-rw-r--r--gold/dynobj.h56
-rw-r--r--gold/gold.cc4
-rw-r--r--gold/object.cc26
-rw-r--r--gold/object.h5
-rw-r--r--gold/options.cc9
-rw-r--r--gold/options.h15
-rw-r--r--gold/parameters.cc4
-rw-r--r--gold/parameters.h10
-rw-r--r--gold/symtab.cc20
-rw-r--r--gold/testsuite/Makefile.am37
-rw-r--r--gold/testsuite/Makefile.in43
-rw-r--r--gold/testsuite/undef_symbol.cc38
-rwxr-xr-xgold/testsuite/undef_symbol.sh45
-rw-r--r--gold/testsuite/undef_symbol_main.cc29
15 files changed, 361 insertions, 75 deletions
diff --git a/gold/dynobj.cc b/gold/dynobj.cc
index 9845194b3b6..a6f3588f48c 100644
--- a/gold/dynobj.cc
+++ b/gold/dynobj.cc
@@ -39,7 +39,9 @@ namespace gold
// see a DT_SONAME entry.
Dynobj::Dynobj(const std::string& name, Input_file* input_file, off_t offset)
- : Object(name, input_file, true, offset)
+ : Object(name, input_file, true, offset),
+ needed_(),
+ unknown_needed_(UNKNOWN_NEEDED_UNSET)
{
// This will be overridden by a DT_SONAME entry, hopefully. But if
// we never see a DT_SONAME entry, our rule is to use the dynamic
@@ -60,14 +62,6 @@ Dynobj::Dynobj(const std::string& name, Input_file* input_file, off_t offset)
}
}
-// Return the string to use in a DT_NEEDED entry.
-
-const char*
-Dynobj::soname() const
-{
- return this->soname_.c_str();
-}
-
// Class Sized_dynobj.
template<int size, bool big_endian>
@@ -193,19 +187,20 @@ Sized_dynobj<size, big_endian>::read_dynsym_section(
*view_info = shdr.get_sh_info();
}
-// Set the soname field if this shared object has a DT_SONAME tag.
-// PSHDRS points to the section headers. DYNAMIC_SHNDX is the section
-// index of the SHT_DYNAMIC section. STRTAB_SHNDX, STRTAB, and
-// STRTAB_SIZE are the section index and contents of a string table
-// which may be the one associated with the SHT_DYNAMIC section.
+// Read the dynamic tags. Set the soname field if this shared object
+// has a DT_SONAME tag. Record the DT_NEEDED tags. PSHDRS points to
+// the section headers. DYNAMIC_SHNDX is the section index of the
+// SHT_DYNAMIC section. STRTAB_SHNDX, STRTAB, and STRTAB_SIZE are the
+// section index and contents of a string table which may be the one
+// associated with the SHT_DYNAMIC section.
template<int size, bool big_endian>
void
-Sized_dynobj<size, big_endian>::set_soname(const unsigned char* pshdrs,
- unsigned int dynamic_shndx,
- unsigned int strtab_shndx,
- const unsigned char* strtabu,
- off_t strtab_size)
+Sized_dynobj<size, big_endian>::read_dynamic(const unsigned char* pshdrs,
+ unsigned int dynamic_shndx,
+ unsigned int strtab_shndx,
+ const unsigned char* strtabu,
+ off_t strtab_size)
{
typename This::Shdr dynamicshdr(pshdrs + dynamic_shndx * This::shdr_size);
gold_assert(dynamicshdr.get_sh_type() == elfcpp::SHT_DYNAMIC);
@@ -236,30 +231,48 @@ Sized_dynobj<size, big_endian>::set_soname(const unsigned char* pshdrs,
strtabu = this->get_view(strtabshdr.get_sh_offset(), strtab_size, false);
}
+ const char* const strtab = reinterpret_cast<const char*>(strtabu);
+
for (const unsigned char* p = pdynamic;
p < pdynamic + dynamic_size;
p += This::dyn_size)
{
typename This::Dyn dyn(p);
- if (dyn.get_d_tag() == elfcpp::DT_SONAME)
+ switch (dyn.get_d_tag())
{
- off_t val = dyn.get_d_val();
- if (val >= strtab_size)
- {
+ case elfcpp::DT_NULL:
+ // We should always see DT_NULL at the end of the dynamic
+ // tags.
+ return;
+
+ case elfcpp::DT_SONAME:
+ {
+ off_t val = dyn.get_d_val();
+ if (val >= strtab_size)
this->error(_("DT_SONAME value out of range: %lld >= %lld"),
- static_cast<long long>(val),
- static_cast<long long>(strtab_size));
- return;
- }
+ static_cast<long long>(val),
+ static_cast<long long>(strtab_size));
+ else
+ this->set_soname_string(strtab + val);
+ }
+ break;
- const char* strtab = reinterpret_cast<const char*>(strtabu);
- this->set_soname_string(strtab + val);
- return;
- }
+ case elfcpp::DT_NEEDED:
+ {
+ off_t val = dyn.get_d_val();
+ if (val >= strtab_size)
+ this->error(_("DT_NEEDED value out of range: %lld >= %lld"),
+ static_cast<long long>(val),
+ static_cast<long long>(strtab_size));
+ else
+ this->add_needed(strtab + val);
+ }
+ break;
- if (dyn.get_d_tag() == elfcpp::DT_NULL)
- return;
+ default:
+ break;
+ }
}
this->error(_("missing DT_NULL in dynamic segment"));
@@ -346,15 +359,15 @@ Sized_dynobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
}
// Read the SHT_DYNAMIC section to find whether this shared object
- // has a DT_SONAME tag. This doesn't really have anything to do
- // with reading the symbols, but this is a convenient place to do
- // it.
+ // has a DT_SONAME tag and to record any DT_NEEDED tags. This
+ // doesn't really have anything to do with reading the symbols, but
+ // this is a convenient place to do it.
if (dynamic_shndx != -1U)
- this->set_soname(pshdrs, dynamic_shndx, strtab_shndx,
- (sd->symbol_names == NULL
- ? NULL
- : sd->symbol_names->data()),
- sd->symbol_names_size);
+ this->read_dynamic(pshdrs, dynamic_shndx, strtab_shndx,
+ (sd->symbol_names == NULL
+ ? NULL
+ : sd->symbol_names->data()),
+ sd->symbol_names_size);
}
// Lay out the input sections for a dynamic object. We don't want to
diff --git a/gold/dynobj.h b/gold/dynobj.h
index eb6eb5c1cf5..1fe37a4b600 100644
--- a/gold/dynobj.h
+++ b/gold/dynobj.h
@@ -39,11 +39,38 @@ class General_options;
class Dynobj : public Object
{
public:
+ // We keep a list of all the DT_NEEDED entries we find.
+ typedef std::vector<std::string> Needed;
+
Dynobj(const std::string& name, Input_file* input_file, off_t offset = 0);
// Return the name to use in a DT_NEEDED entry for this object.
const char*
- soname() const;
+ soname() const
+ { return this->soname_.c_str(); }
+
+ // Return the list of DT_NEEDED strings.
+ const Needed&
+ needed() const
+ { return this->needed_; }
+
+ // Return whether this dynamic object has any DT_NEEDED entries
+ // which were not seen during the link.
+ bool
+ has_unknown_needed_entries() const
+ {
+ gold_assert(this->unknown_needed_ != UNKNOWN_NEEDED_UNSET);
+ return this->unknown_needed_ == UNKNOWN_NEEDED_TRUE;
+ }
+
+ // Set whether this dynamic object has any DT_NEEDED entries which
+ // were not seen during the link.
+ void
+ set_has_unknown_needed_entries(bool set)
+ {
+ gold_assert(this->unknown_needed_ == UNKNOWN_NEEDED_UNSET);
+ this->unknown_needed_ = set ? UNKNOWN_NEEDED_TRUE : UNKNOWN_NEEDED_FALSE;
+ }
// Compute the ELF hash code for a string.
static uint32_t
@@ -74,6 +101,11 @@ class Dynobj : public Object
set_soname_string(const char* s)
{ this->soname_.assign(s); }
+ // Add an entry to the list of DT_NEEDED strings.
+ void
+ add_needed(const char* s)
+ { this->needed_.push_back(std::string(s)); }
+
private:
// Compute the GNU hash code for a string.
static uint32_t
@@ -101,8 +133,21 @@ class Dynobj : public Object
unsigned char** pphash,
unsigned int* phashlen);
+ // Values for the has_unknown_needed_entries_ field.
+ enum Unknown_needed
+ {
+ UNKNOWN_NEEDED_UNSET,
+ UNKNOWN_NEEDED_TRUE,
+ UNKNOWN_NEEDED_FALSE
+ };
+
// The DT_SONAME name, if any.
std::string soname_;
+ // The list of DT_NEEDED entries.
+ Needed needed_;
+ // Whether this dynamic object has any DT_NEEDED entries not seen
+ // during the link.
+ Unknown_needed unknown_needed_;
};
// A dynamic object, size and endian specific version.
@@ -187,12 +232,11 @@ class Sized_dynobj : public Dynobj
File_view** view, off_t* view_size,
unsigned int* view_info);
- // Set the SONAME from the SHT_DYNAMIC section at DYNAMIC_SHNDX.
- // The STRTAB parameters may have the relevant string table.
+ // Read the dynamic tags.
void
- set_soname(const unsigned char* pshdrs, unsigned int dynamic_shndx,
- unsigned int strtab_shndx, const unsigned char* strtabu,
- off_t strtab_size);
+ read_dynamic(const unsigned char* pshdrs, unsigned int dynamic_shndx,
+ unsigned int strtab_shndx, const unsigned char* strtabu,
+ off_t strtab_size);
// Mapping from version number to version name.
typedef std::vector<const char*> Version_map;
diff --git a/gold/gold.cc b/gold/gold.cc
index a215d0f31dc..d0c2095c3f0 100644
--- a/gold/gold.cc
+++ b/gold/gold.cc
@@ -180,6 +180,10 @@ queue_middle_tasks(const General_options& options,
(*input_objects->dynobj_begin())->name().c_str());
}
+ // For each dynamic object, record whether we've seen all the
+ // dynamic objects that it depends upon.
+ input_objects->check_dynamic_dependencies();
+
// See if any of the input definitions violate the One Definition Rule.
// TODO: if this is too slow, do this as a task, rather than inline.
symtab->detect_odr_violations();
diff --git a/gold/object.cc b/gold/object.cc
index c0e2acd4e77..fa16d138549 100644
--- a/gold/object.cc
+++ b/gold/object.cc
@@ -1080,6 +1080,32 @@ Input_objects::add_object(Object* obj)
return true;
}
+// For each dynamic object, record whether we've seen all of its
+// explicit dependencies.
+
+void
+Input_objects::check_dynamic_dependencies() const
+{
+ for (Dynobj_list::const_iterator p = this->dynobj_list_.begin();
+ p != this->dynobj_list_.end();
+ ++p)
+ {
+ const Dynobj::Needed& needed((*p)->needed());
+ bool found_all = true;
+ for (Dynobj::Needed::const_iterator pneeded = needed.begin();
+ pneeded != needed.end();
+ ++pneeded)
+ {
+ if (this->sonames_.find(*pneeded) == this->sonames_.end())
+ {
+ found_all = false;
+ break;
+ }
+ }
+ (*p)->set_has_unknown_needed_entries(!found_all);
+ }
+}
+
// Relocate_info methods.
// Return a string describing the location of a relocation. This is
diff --git a/gold/object.h b/gold/object.h
index b8bc65c9098..332739295f2 100644
--- a/gold/object.h
+++ b/gold/object.h
@@ -951,6 +951,11 @@ class Input_objects
target() const
{ return this->target_; }
+ // For each dynamic object, check whether we've seen all of its
+ // explicit dependencies.
+ void
+ check_dynamic_dependencies() const;
+
// Iterate over all regular objects.
Relobj_iterator
diff --git a/gold/options.cc b/gold/options.cc
index d4d9e16cc2b..69f452dd380 100644
--- a/gold/options.cc
+++ b/gold/options.cc
@@ -337,6 +337,14 @@ namespace gold
const options::One_option
options::Command_line_options::options[] =
{
+ GENERAL_NOARG('\0', "allow-shlib-undefined",
+ N_("Allow unresolved references in shared libraries"),
+ NULL, TWO_DASHES,
+ &General_options::set_allow_shlib_undefined),
+ GENERAL_NOARG('\0', "no-allow-shlib-undefined",
+ N_("Do not allow unresolved references in shared libraries"),
+ NULL, TWO_DASHES,
+ &General_options::set_no_allow_shlib_undefined),
POSDEP_NOARG('\0', "as-needed",
N_("Only set DT_NEEDED for dynamic libs if used"),
NULL, TWO_DASHES, &Position_dependent_options::set_as_needed),
@@ -475,6 +483,7 @@ General_options::General_options()
output_file_name_("a.out"),
is_relocatable_(false),
strip_(STRIP_NONE),
+ allow_shlib_undefined_(false),
symbolic_(false),
detect_odr_violations_(false),
create_eh_frame_hdr_(false),
diff --git a/gold/options.h b/gold/options.h
index c54af7781d2..b5d2328a1a6 100644
--- a/gold/options.h
+++ b/gold/options.h
@@ -148,6 +148,12 @@ class General_options
strip_debug() const
{ return this->strip_ == STRIP_ALL || this->strip_ == STRIP_DEBUG; }
+ // --allow-shlib-undefined: do not warn about unresolved symbols in
+ // --shared libraries.
+ bool
+ allow_shlib_undefined() const
+ { return this->allow_shlib_undefined_; }
+
// -Bsymbolic: bind defined symbols locally.
bool
symbolic() const
@@ -301,6 +307,14 @@ class General_options
{ this->strip_ = STRIP_DEBUG; }
void
+ set_allow_shlib_undefined()
+ { this->allow_shlib_undefined_ = true; }
+
+ void
+ set_no_allow_shlib_undefined()
+ { this->allow_shlib_undefined_ = false; }
+
+ void
set_symbolic()
{ this->symbolic_ = true; }
@@ -420,6 +434,7 @@ class General_options
const char* output_file_name_;
bool is_relocatable_;
Strip strip_;
+ bool allow_shlib_undefined_;
bool symbolic_;
bool detect_odr_violations_;
bool create_eh_frame_hdr_;
diff --git a/gold/parameters.cc b/gold/parameters.cc
index f332134d4b6..b96221a8b24 100644
--- a/gold/parameters.cc
+++ b/gold/parameters.cc
@@ -33,7 +33,8 @@ namespace gold
Parameters::Parameters(Errors* errors)
: errors_(errors), output_file_name_(NULL),
output_file_type_(OUTPUT_INVALID), sysroot_(),
- strip_(STRIP_INVALID), symbolic_(false), detect_odr_violations_(false),
+ strip_(STRIP_INVALID), allow_shlib_undefined_(false),
+ symbolic_(false), detect_odr_violations_(false),
optimization_level_(0), export_dynamic_(false),
is_doing_static_link_valid_(false), doing_static_link_(false),
is_size_and_endian_valid_(false), size_(0), is_big_endian_(false)
@@ -47,6 +48,7 @@ Parameters::set_from_options(const General_options* options)
{
this->output_file_name_ = options->output_file_name();
this->sysroot_ = options->sysroot();
+ this->allow_shlib_undefined_ = options->allow_shlib_undefined();
this->symbolic_ = options->symbolic();
this->detect_odr_violations_ = options->detect_odr_violations();
this->optimization_level_ = options->optimization_level();
diff --git a/gold/parameters.h b/gold/parameters.h
index 353f01f2ba8..ee60b10b4a0 100644
--- a/gold/parameters.h
+++ b/gold/parameters.h
@@ -112,6 +112,14 @@ class Parameters
return this->strip_ == STRIP_ALL || this->strip_ == STRIP_DEBUG;
}
+ // Whether to permit unresolved references from shared libraries.
+ bool
+ allow_shlib_undefined() const
+ {
+ gold_assert(this->options_valid_);
+ return this->allow_shlib_undefined_;
+ }
+
// Whether we are doing a symbolic link, in which all defined
// symbols are bound locally.
bool
@@ -224,6 +232,8 @@ class Parameters
std::string sysroot_;
// Which symbols to strip.
Strip strip_;
+ // Whether to allow undefined references from shared libraries.
+ bool allow_shlib_undefined_;
// Whether we are doing a symbolic link.
bool symbolic_;
// Whether we try to detect One Definition Rule violations.
diff --git a/gold/symtab.cc b/gold/symtab.cc
index 9bee2837db3..f5e21322b78 100644
--- a/gold/symtab.cc
+++ b/gold/symtab.cc
@@ -1599,6 +1599,26 @@ Symbol_table::sized_write_globals(const Target* target,
{
Sized_symbol<size>* sym = static_cast<Sized_symbol<size>*>(p->second);
+ // Optionally check for unresolved symbols in shared libraries.
+ // This is controlled by the --allow-shlib-undefined option. We
+ // only warn about libraries for which we have seen all the
+ // DT_NEEDED entries. We don't try to track down DT_NEEDED
+ // entries which were not seen in this link. If we didn't see a
+ // DT_NEEDED entry, we aren't going to be able to reliably
+ // report whether the symbol is undefined.
+ if (sym->source() == Symbol::FROM_OBJECT
+ && sym->object()->is_dynamic()
+ && sym->shndx() == elfcpp::SHN_UNDEF
+ && sym->binding() != elfcpp::STB_WEAK
+ && !parameters->allow_shlib_undefined())
+ {
+ // A very ugly cast.
+ Dynobj* dynobj = static_cast<Dynobj*>(sym->object());
+ if (!dynobj->has_unknown_needed_entries())
+ gold_error(_("%s: undefined reference to '%s'"),
+ sym->object()->name().c_str(), sym->name());
+ }
+
unsigned int sym_index = sym->symtab_index();
unsigned int dynsym_index;
if (dynamic_view == NULL)
diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am
index e193fc56028..520863fba6c 100644
--- a/gold/testsuite/Makefile.am
+++ b/gold/testsuite/Makefile.am
@@ -28,11 +28,11 @@ TESTS = object_unittest
if GCC
-TESTS += debug_msg.sh
+if NATIVE_LINKER
-check_DATA += debug_msg.err
+TESTS += debug_msg.sh undef_symbol.sh
-if NATIVE_LINKER
+check_DATA += debug_msg.err undef_symbol.err
NATIVE_PROGS = \
constructor_test \
@@ -116,25 +116,38 @@ object_unittest_SOURCES = object_unittest.cc
if GCC
+if NATIVE_LINKER
+
+gcctestdir/ld: ../ld-new
+ test -d gcctestdir || mkdir -p gcctestdir
+ rm -f gcctestdir/ld
+ (cd gcctestdir && $(LN_S) ../../ld-new ld)
+
debug_msg.o: debug_msg.cc
$(CXXCOMPILE) -O0 -g -c -w -o $@ $(srcdir)/debug_msg.cc
odr_violation1.o: odr_violation1.cc
$(CXXCOMPILE) -O0 -g -c -w -o $@ $(srcdir)/odr_violation1.cc
odr_violation2.o: odr_violation2.cc
$(CXXCOMPILE) -O0 -g -c -w -o $@ $(srcdir)/odr_violation2.cc
-debug_msg.err: debug_msg.o odr_violation1.o odr_violation2.o
- if $(CXXLINK) -Bgcctestdir/ -Wl,--detect-odr-violations -o debug_msg debug_msg.o odr_violation1.o odr_violation2.o 2>$@; \
+debug_msg.err: debug_msg.o odr_violation1.o odr_violation2.o gcctestdir/ld
+ @echo $(CXXLINK) -Bgcctestdir/ -Wl,--detect-odr-violations -o debug_msg debug_msg.o odr_violation1.o odr_violation2.o "2>$@"
+ @if $(CXXLINK) -Bgcctestdir/ -Wl,--detect-odr-violations -o debug_msg debug_msg.o odr_violation1.o odr_violation2.o 2>$@; \
then \
- echo 2>&1 "Link of debug_msg.o should have failed"; \
+ echo 1>&2 "Link of debug_msg.o should have failed"; \
exit 1; \
fi
-if NATIVE_LINKER
-
-gcctestdir/ld: ../ld-new
- test -d gcctestdir || mkdir -p gcctestdir
- rm -f gcctestdir/ld
- (cd gcctestdir && $(LN_S) ../../ld-new ld)
+undef_symbol.o: undef_symbol.cc
+ $(CXXCOMPILE) -O0 -g -c -fPIC $<
+undef_symbol.so: undef_symbol.o
+ $(CXXLINK) -shared undef_symbol.o
+undef_symbol.err: undef_symbol_main.o undef_symbol.so gcctestdir/ld
+ @echo $(CXXLINK) -Bgcctestdir/ -o undef_symbol_test undef_symbol_main.o undef_symbol.so "2>$@"
+ @if $(CXXLINK) -Bgcctestdir/ -o undef_symbol_test undef_symbol_main.o undef_symbol.so 2>$@; \
+ then \
+ echo 1>&2 "Link of undef_symbol_test should have failed"; \
+ exit 1; \
+ fi
# Override the default CXXFLAGS--we don't want any optimization
basic_test.o: basic_test.cc
diff --git a/gold/testsuite/Makefile.in b/gold/testsuite/Makefile.in
index dcbbf520044..b2014239169 100644
--- a/gold/testsuite/Makefile.in
+++ b/gold/testsuite/Makefile.in
@@ -42,8 +42,8 @@ POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
target_triplet = @target@
-@GCC_TRUE@am__append_1 = debug_msg.sh
-@GCC_TRUE@am__append_2 = debug_msg.err
+@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_1 = debug_msg.sh undef_symbol.sh
+@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_2 = debug_msg.err undef_symbol.err
@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_3 = \
@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_shared_1_nonpic_test \
@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_shared_2_nonpic_test \
@@ -1204,24 +1204,37 @@ uninstall-am: uninstall-info-am
tags uninstall uninstall-am uninstall-info-am
-@GCC_TRUE@debug_msg.o: debug_msg.cc
-@GCC_TRUE@ $(CXXCOMPILE) -O0 -g -c -w -o $@ $(srcdir)/debug_msg.cc
-@GCC_TRUE@odr_violation1.o: odr_violation1.cc
-@GCC_TRUE@ $(CXXCOMPILE) -O0 -g -c -w -o $@ $(srcdir)/odr_violation1.cc
-@GCC_TRUE@odr_violation2.o: odr_violation2.cc
-@GCC_TRUE@ $(CXXCOMPILE) -O0 -g -c -w -o $@ $(srcdir)/odr_violation2.cc
-@GCC_TRUE@debug_msg.err: debug_msg.o odr_violation1.o odr_violation2.o
-@GCC_TRUE@ if $(CXXLINK) -Bgcctestdir/ -Wl,--detect-odr-violations -o debug_msg debug_msg.o odr_violation1.o odr_violation2.o 2>$@; \
-@GCC_TRUE@ then \
-@GCC_TRUE@ echo 2>&1 "Link of debug_msg.o should have failed"; \
-@GCC_TRUE@ exit 1; \
-@GCC_TRUE@ fi
-
@GCC_TRUE@@NATIVE_LINKER_TRUE@gcctestdir/ld: ../ld-new
@GCC_TRUE@@NATIVE_LINKER_TRUE@ test -d gcctestdir || mkdir -p gcctestdir
@GCC_TRUE@@NATIVE_LINKER_TRUE@ rm -f gcctestdir/ld
@GCC_TRUE@@NATIVE_LINKER_TRUE@ (cd gcctestdir && $(LN_S) ../../ld-new ld)
+@GCC_TRUE@@NATIVE_LINKER_TRUE@debug_msg.o: debug_msg.cc
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -O0 -g -c -w -o $@ $(srcdir)/debug_msg.cc
+@GCC_TRUE@@NATIVE_LINKER_TRUE@odr_violation1.o: odr_violation1.cc
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -O0 -g -c -w -o $@ $(srcdir)/odr_violation1.cc
+@GCC_TRUE@@NATIVE_LINKER_TRUE@odr_violation2.o: odr_violation2.cc
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -O0 -g -c -w -o $@ $(srcdir)/odr_violation2.cc
+@GCC_TRUE@@NATIVE_LINKER_TRUE@debug_msg.err: debug_msg.o odr_violation1.o odr_violation2.o gcctestdir/ld
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ @echo $(CXXLINK) -Bgcctestdir/ -Wl,--detect-odr-violations -o debug_msg debug_msg.o odr_violation1.o odr_violation2.o "2>$@"
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ @if $(CXXLINK) -Bgcctestdir/ -Wl,--detect-odr-violations -o debug_msg debug_msg.o odr_violation1.o odr_violation2.o 2>$@; \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ then \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ echo 1>&2 "Link of debug_msg.o should have failed"; \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ exit 1; \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ fi
+
+@GCC_TRUE@@NATIVE_LINKER_TRUE@undef_symbol.o: undef_symbol.cc
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -O0 -g -c -fPIC $<
+@GCC_TRUE@@NATIVE_LINKER_TRUE@undef_symbol.so: undef_symbol.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -shared undef_symbol.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@undef_symbol.err: undef_symbol_main.o undef_symbol.so gcctestdir/ld
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ @echo $(CXXLINK) -Bgcctestdir/ -o undef_symbol_test undef_symbol_main.o undef_symbol.so "2>$@"
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ @if $(CXXLINK) -Bgcctestdir/ -o undef_symbol_test undef_symbol_main.o undef_symbol.so 2>$@; \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ then \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ echo 1>&2 "Link of undef_symbol_test should have failed"; \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ exit 1; \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ fi
+
# Override the default CXXFLAGS--we don't want any optimization
@GCC_TRUE@@NATIVE_LINKER_TRUE@basic_test.o: basic_test.cc
@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -O0 -c -o $@ $<
diff --git a/gold/testsuite/undef_symbol.cc b/gold/testsuite/undef_symbol.cc
new file mode 100644
index 00000000000..25fd1813f38
--- /dev/null
+++ b/gold/testsuite/undef_symbol.cc
@@ -0,0 +1,38 @@
+// undef_symbol.cc -- a test case for undefined references
+
+// Copyright 2007 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 is constructed to have an undefined reference to the
+// global variable a. We should get an error when we link.
+
+extern int a;
+
+class Foo
+{
+ public:
+ Foo()
+ : a_(a)
+ { }
+ private:
+ int a_;
+};
+
+static Foo foo;
diff --git a/gold/testsuite/undef_symbol.sh b/gold/testsuite/undef_symbol.sh
new file mode 100755
index 00000000000..bc9eb570e1a
--- /dev/null
+++ b/gold/testsuite/undef_symbol.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+# undef_symbol.sh -- a test case for undefined symbols in shared libraries
+
+# Copyright 2007 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 debug_msg.cc, a C++ source file constructed to
+# have undefined references. We compile that file with debug
+# information and then try to link it, and make sure the proper errors
+# are displayed. The errors will be found in debug_msg.err.
+
+check()
+{
+ if ! grep -q "$1" undef_symbol.err
+ then
+ echo "Did not find expected error:"
+ echo " $1"
+ echo ""
+ echo "Actual error output below:"
+ cat undef_symbol.err
+ exit 1
+ fi
+}
+
+check "undef_symbol.so: undefined reference to 'a'"
+
+exit 0
diff --git a/gold/testsuite/undef_symbol_main.cc b/gold/testsuite/undef_symbol_main.cc
new file mode 100644
index 00000000000..90f97e07a59
--- /dev/null
+++ b/gold/testsuite/undef_symbol_main.cc
@@ -0,0 +1,29 @@
+// undef_symbol_1.cc -- a test case for undefined references
+
+// Copyright 2007 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.
+
+// Main function for undefined symbol test.
+
+int
+main()
+{
+ return 0;
+}