+2009-05-19 Doug Kwan <dougkwan@google.com>
+
+ * archive.cc (Archive::Archive): Move constructor from archive.h
+ to here. Initialize no_export_.
+ (Archive::get_elf_object_for_member): Set no_export flag of object.
+ * archive.h (Archive::Archive): Move constructor body to
+ archive.cc.
+ (Archive::no_export): New method.
+ (Archive::no_export_): New field.
+ * object.h (Object::Object): Initialize no_export_ to false.
+ (Object::no_export, Object::set_no_export): New methods.
+ (Object::no_export_): New field.
+ * options.cc (General_options::parse_exclude_libs): New method.
+ (General_options::check_excluded_libs) Same.
+ * options.h (exclude_libs): New option.
+ (General_options::check_excluded_libs): New method declaration.
+ (General_options::excluded_libs_): New field.
+ * symtab.cc (Symbol_table::add_from_relobj): Hide symbols with
+ default or protected visibility if an object has no-export flag set.
+ testsuite/Makefile.am (check_PROGRAMS): Add exclude_libs_test.
+ (check_SCRIPTS): Add exclude_libs_test.sh.
+ (check_DATA): Add exclude_libs_test.syms.
+ (MOSTLYCLEANFILES): Add exclude_libs_test.syms,
+ libexclude_libs_test_1.a and libexclude_libs_test_2.a.
+ (exclude_libs_test_SOURCES, exclude_libs_test_DEPENDENCIES,
+ exclude_libs_test_LDFLAGS and exclude_libs_test_LDADD): Define.
+ (exclude_libs_test.syms, libexclude_libs_test_1.a,
+ libexclude_libs_test_2.a): New rules.
+ * testsuite/Makefile.in: Regenerate.
+ * testsuite/exclude_libs_test.c: New file.
+ * testsuite/exclude_libs_test.sh: Ditto.
+ * testsuite/exclude_libs_test_1.c: Ditto.
+ * testsuite/exclude_libs_test_2.c: Ditto.
+
2009-05-15 Ian Lance Taylor <iant@google.com>
* configure.ac: Check for declarations for cases where libiberty.h
const char Archive::arfmag[2] = { '`', '\n' };
+Archive::Archive(const std::string& name, Input_file* input_file,
+ bool is_thin_archive, Dirsearch* dirpath, Task* task)
+ : name_(name), input_file_(input_file), armap_(), armap_names_(),
+ extended_names_(), armap_checked_(), seen_offsets_(), members_(),
+ is_thin_archive_(is_thin_archive), included_member_(false),
+ nested_archives_(), dirpath_(dirpath), task_(task), num_members_(0)
+{
+ this->no_export_ =
+ parameters->options().check_excluded_libs(input_file->found_name());
+}
+
// Set up the archive: read the symbol map and the extended name
// table.
return NULL;
}
- return make_elf_object((std::string(this->input_file_->filename())
- + "(" + member_name + ")"),
- input_file, memoff, ehdr, read_size,
- punconfigured);
+ Object *obj = make_elf_object((std::string(this->input_file_->filename())
+ + "(" + member_name + ")"),
+ input_file, memoff, ehdr, read_size,
+ punconfigured);
+ obj->set_no_export(this->no_export());
+ return obj;
}
// Read the symbols from all the archive members in the link.
{
public:
Archive(const std::string& name, Input_file* input_file,
- bool is_thin_archive, Dirsearch* dirpath, Task* task)
- : name_(name), input_file_(input_file), armap_(), armap_names_(),
- extended_names_(), armap_checked_(), seen_offsets_(), members_(),
- is_thin_archive_(is_thin_archive), included_member_(false),
- nested_archives_(), dirpath_(dirpath), task_(task), num_members_(0)
- { }
+ bool is_thin_archive, Dirsearch* dirpath, Task* task);
// The length of the magic string at the start of an archive.
static const int sarmag = 8;
size_t
count_members();
+ // Return the no-export flag.
+ bool
+ no_export()
+ { return this->no_export_; }
+
private:
Archive(const Archive&);
Archive& operator=(const Archive&);
Task *task_;
// Number of members in this archive;
unsigned int num_members_;
+ // True if we exclude this library archive from automatic export.
+ bool no_export_;
};
// This class is used to read an archive and pick out the desired
Object(const std::string& name, Input_file* input_file, bool is_dynamic,
off_t offset = 0)
: name_(name), input_file_(input_file), offset_(offset), shnum_(-1U),
- is_dynamic_(is_dynamic), target_(NULL), xindex_(NULL)
+ is_dynamic_(is_dynamic), target_(NULL), xindex_(NULL), no_export_(false)
{ input_file->file().add_object(); }
virtual ~Object()
searched_for() const
{ return this->input_file()->will_search_for(); }
+ bool
+ no_export() const
+ { return this->no_export_; }
+
+ void
+ set_no_export(bool value)
+ { this->no_export_ = value; }
+
protected:
// Returns NULL for Objects that are not plugin objects. This method
// is overridden in the Pluginobj class.
Target* target_;
// Many sections for objects with more than SHN_LORESERVE sections.
Xindex* xindex_;
+ // True if exclude this object from automatic symbol export.
+ // This is used only for archive objects.
+ bool no_export_;
};
// Implement sized_target inline for efficiency. This approach breaks
cmdline->inputs().end_group();
}
+// The function add_excluded_libs() in ld/ldlang.c of GNU ld breaks up a list
+// of names seperated by commas or semi-colons and puts them in a linked list.
+// We implement the same parsing of names here but store names in an unordered
+// map to speed up searching of names.
+
+void
+General_options::parse_exclude_libs(const char*, const char* arg,
+ Command_line*)
+{
+ const char *p = arg;
+
+ while (*p != '\0')
+ {
+ size_t length = strcspn(p, ",:");
+ this->excluded_libs_.insert(std::string(p, length));
+ p += (p[length] ? length + 1 : length);
+ }
+}
+
+// The checking logic is based on the function check_excluded_libs() in
+// ld/ldlang.c of GNU ld but our implementation is different because we use
+// an unordered map instead of a linked list, which is what GNU ld uses. GNU
+// ld searches sequentially in the excluded libs list. For a given archive,
+// a match is found if the archive's name matches exactly one of the list
+// entry or if the archive's name is of the form FOO.a and FOO matches exactly
+// one of the list entry. An entry "ALL" in the list is considered as a
+// wild-card and matches any given name.
+
+bool
+General_options::check_excluded_libs (const std::string &name) const
+{
+ Unordered_set<std::string>::const_iterator p;
+
+ // Exit early for the most common case.
+ if (excluded_libs_.empty())
+ return false;
+
+ // If we see "ALL", all archives are excluded from automatic export.
+ p = excluded_libs_.find(std::string("ALL"));
+ if (p != excluded_libs_.end())
+ return true;
+
+ // Try finding an exact match.
+ p = excluded_libs_.find(name);
+ if (p != excluded_libs_.end())
+ return true;
+
+ // Try matching NAME without ".a" at the end.
+ size_t length = name.length();
+ if ((length >= 2)
+ && (name[length-2] == '.')
+ && (name[length-1] == 'a'))
+ {
+ p = excluded_libs_.find(name.substr(0, length - 2));
+ if (p != excluded_libs_.end())
+ return true;
+ }
+
+ return false;
+}
+
} // End namespace gold.
namespace
DEFINE_string(entry, options::TWO_DASHES, 'e', NULL,
N_("Set program start address"), N_("ADDRESS"));
+ DEFINE_special(exclude_libs, options::TWO_DASHES, '\0',
+ N_("Exclude libraries from automatic export"),
+ N_(("lib,lib ...")));
+
DEFINE_bool(export_dynamic, options::TWO_DASHES, 'E', false,
N_("Export all dynamic symbols"),
N_("Do not export all dynamic symbols (default)"));
incremental_disposition() const
{ return this->incremental_disposition_; }
+ // Return true if S is the name of a library excluded from automatic
+ // symbol export.
+ bool
+ check_excluded_libs (const std::string &s) const;
+
private:
// Don't copy this structure.
General_options(const General_options&);
// build (--incremental-changed, --incremental-unchanged or
// --incremental-unknown)
bool implicit_incremental_;
+ // Libraries excluded from automatic export via --exclude-libs
+ Unordered_set<std::string> excluded_libs_;
};
// The position-dependent options. We use this to store the state of
psym = &sym2;
}
+ // Fix up visibility if object has no-export set.
+ if (relobj->no_export())
+ {
+ // We may have copied symbol already above.
+ if (psym != &sym2)
+ {
+ memcpy(symbuf, p, sym_size);
+ psym = &sym2;
+ }
+
+ elfcpp::STV visibility = sym2.get_st_visibility();
+ if (visibility == elfcpp::STV_DEFAULT
+ || visibility == elfcpp::STV_PROTECTED)
+ {
+ elfcpp::Sym_write<size, big_endian> sw(symbuf);
+ unsigned char nonvis = sym2.get_st_nonvis();
+ sw.put_st_other(elfcpp::STV_HIDDEN, nonvis);
+ }
+ }
+
Stringpool::Key name_key;
name = this->namepool_.add_with_length(name, namelen, true,
&name_key);
endif PLUGINS
+check_PROGRAMS += exclude_libs_test
+check_SCRIPTS += exclude_libs_test.sh
+check_DATA += exclude_libs_test.syms
+MOSTLYCLEANFILES += exclude_libs_test.syms libexclude_libs_test_1.a \
+ libexclude_libs_test_2.a
+exclude_libs_test_SOURCES = exclude_libs_test.c
+exclude_libs_test_DEPENDENCIES = gcctestdir/ld libexclude_libs_test_1.a \
+ libexclude_libs_test_2.a
+exclude_libs_test_LDFLAGS = -Bgcctestdir/ -L. \
+ -Wl,--exclude-libs,dummy:libexclude_libs_test_1
+exclude_libs_test_LDADD = -lexclude_libs_test_1 -lexclude_libs_test_2
+exclude_libs_test.syms: exclude_libs_test
+ $(TEST_READELF) -sW $< >$@ 2>/dev/null
+libexclude_libs_test_1.a: exclude_libs_test_1.o
+ $(TEST_AR) rc $@ $^
+libexclude_libs_test_2.a: exclude_libs_test_2.o
+ $(TEST_AR) rc $@ $^
+
endif GCC
endif NATIVE_LINKER
$(am__EXEEXT_7) $(am__EXEEXT_8) $(am__EXEEXT_9) \
$(am__EXEEXT_10) $(am__EXEEXT_11) $(am__EXEEXT_12) \
$(am__EXEEXT_13) $(am__EXEEXT_14) $(am__EXEEXT_15) \
- $(am__EXEEXT_16)
+ $(am__EXEEXT_16) $(am__EXEEXT_17)
@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_1 = basic_test \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ basic_static_test basic_pic_test \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ basic_static_pic_test \
@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ plugin_test_2.err \
@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ plugin_test_3.err \
@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ plugin_test_4.err
+@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_24 = exclude_libs_test
+@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_25 = exclude_libs_test.sh
+@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_26 = exclude_libs_test.syms
+@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_27 = exclude_libs_test.syms libexclude_libs_test_1.a \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ libexclude_libs_test_2.a
+
+@GCC_FALSE@exclude_libs_test_DEPENDENCIES =
+@NATIVE_LINKER_FALSE@exclude_libs_test_DEPENDENCIES =
subdir = testsuite
DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ plugin_test_2$(EXEEXT) \
@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ plugin_test_3$(EXEEXT) \
@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ plugin_test_4$(EXEEXT)
+@GCC_TRUE@@NATIVE_LINKER_TRUE@am__EXEEXT_17 = \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ exclude_libs_test$(EXEEXT)
basic_pic_test_SOURCES = basic_pic_test.c
basic_pic_test_OBJECTS = basic_pic_test.$(OBJEXT)
basic_pic_test_LDADD = $(LDADD)
@GCC_TRUE@@NATIVE_LINKER_TRUE@ exception_test_2.$(OBJEXT)
exception_test_OBJECTS = $(am_exception_test_OBJECTS)
exception_test_LDADD = $(LDADD)
+am__exclude_libs_test_SOURCES_DIST = exclude_libs_test.c
+@GCC_TRUE@@NATIVE_LINKER_TRUE@am_exclude_libs_test_OBJECTS = \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ exclude_libs_test.$(OBJEXT)
+exclude_libs_test_OBJECTS = $(am_exclude_libs_test_OBJECTS)
flagstest_compress_debug_sections_SOURCES = \
flagstest_compress_debug_sections.c
flagstest_compress_debug_sections_OBJECTS = \
$(exception_shared_1_test_SOURCES) \
$(exception_shared_2_test_SOURCES) \
$(exception_static_test_SOURCES) $(exception_test_SOURCES) \
+ $(exclude_libs_test_SOURCES) \
flagstest_compress_debug_sections.c flagstest_o_specialfile.c \
flagstest_o_specialfile_and_compress_debug_sections.c \
$(initpri1_SOURCES) $(justsyms_SOURCES) many_sections_r_test.c \
$(am__exception_shared_2_test_SOURCES_DIST) \
$(am__exception_static_test_SOURCES_DIST) \
$(am__exception_test_SOURCES_DIST) \
+ $(am__exclude_libs_test_SOURCES_DIST) \
flagstest_compress_debug_sections.c flagstest_o_specialfile.c \
flagstest_o_specialfile_and_compress_debug_sections.c \
$(am__initpri1_SOURCES_DIST) $(am__justsyms_SOURCES_DIST) \
# .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
# the right choice for files 'make' builds that people rebuild.
-MOSTLYCLEANFILES = *.so $(am__append_16) $(am__append_23)
+MOSTLYCLEANFILES = *.so $(am__append_16) $(am__append_23) \
+ $(am__append_27)
# We will add to these later, for each individual test. Note
# that we add each test under check_SCRIPTS or check_PROGRAMS;
# the TESTS variable is automatically populated from these.
-check_SCRIPTS = $(am__append_2) $(am__append_21)
-check_DATA = $(am__append_3) $(am__append_22)
+check_SCRIPTS = $(am__append_2) $(am__append_21) $(am__append_25)
+check_DATA = $(am__append_3) $(am__append_22) $(am__append_26)
BUILT_SOURCES = $(am__append_14)
TESTS = $(check_SCRIPTS) $(check_PROGRAMS)
@GCC_TRUE@@NATIVE_LINKER_TRUE@thin_archive_test_2_DEPENDENCIES = gcctestdir/ld libthinall.a
@GCC_TRUE@@NATIVE_LINKER_TRUE@thin_archive_test_2_LDFLAGS = -Bgcctestdir/ -L.
@GCC_TRUE@@NATIVE_LINKER_TRUE@thin_archive_test_2_LDADD = -lthinall
+@GCC_TRUE@@NATIVE_LINKER_TRUE@exclude_libs_test_SOURCES = exclude_libs_test.c
+@GCC_TRUE@@NATIVE_LINKER_TRUE@exclude_libs_test_DEPENDENCIES = gcctestdir/ld libexclude_libs_test_1.a \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ libexclude_libs_test_2.a
+
+@GCC_TRUE@@NATIVE_LINKER_TRUE@exclude_libs_test_LDFLAGS = -Bgcctestdir/ -L. \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ -Wl,--exclude-libs,dummy:libexclude_libs_test_1
+
+@GCC_TRUE@@NATIVE_LINKER_TRUE@exclude_libs_test_LDADD = -lexclude_libs_test_1 -lexclude_libs_test_2
all: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) all-am
exit 1;; \
esac; \
done; \
- echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign testsuite/Makefile'; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu testsuite/Makefile'; \
cd $(top_srcdir) && \
- $(AUTOMAKE) --foreign testsuite/Makefile
+ $(AUTOMAKE) --gnu testsuite/Makefile
.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
exception_test$(EXEEXT): $(exception_test_OBJECTS) $(exception_test_DEPENDENCIES)
@rm -f exception_test$(EXEEXT)
$(CXXLINK) $(exception_test_LDFLAGS) $(exception_test_OBJECTS) $(exception_test_LDADD) $(LIBS)
+exclude_libs_test$(EXEEXT): $(exclude_libs_test_OBJECTS) $(exclude_libs_test_DEPENDENCIES)
+ @rm -f exclude_libs_test$(EXEEXT)
+ $(LINK) $(exclude_libs_test_LDFLAGS) $(exclude_libs_test_OBJECTS) $(exclude_libs_test_LDADD) $(LIBS)
@GCC_FALSE@flagstest_compress_debug_sections$(EXEEXT): $(flagstest_compress_debug_sections_OBJECTS) $(flagstest_compress_debug_sections_DEPENDENCIES)
@GCC_FALSE@ @rm -f flagstest_compress_debug_sections$(EXEEXT)
@GCC_FALSE@ $(LINK) $(flagstest_compress_debug_sections_LDFLAGS) $(flagstest_compress_debug_sections_OBJECTS) $(flagstest_compress_debug_sections_LDADD) $(LIBS)
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/exception_test_1.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/exception_test_2.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/exception_test_main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/exclude_libs_test.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flagstest_compress_debug_sections.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flagstest_o_specialfile.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flagstest_o_specialfile_and_compress_debug_sections.Po@am__quote@
@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@empty.syms:
@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ @echo "" >empty.syms
@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ @echo "Symbol table" >>empty.syms
+@GCC_TRUE@@NATIVE_LINKER_TRUE@exclude_libs_test.syms: exclude_libs_test
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(TEST_READELF) -sW $< >$@ 2>/dev/null
+@GCC_TRUE@@NATIVE_LINKER_TRUE@libexclude_libs_test_1.a: exclude_libs_test_1.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(TEST_AR) rc $@ $^
+@GCC_TRUE@@NATIVE_LINKER_TRUE@libexclude_libs_test_2.a: exclude_libs_test_2.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(TEST_AR) rc $@ $^
# 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.
.NOEXPORT:
--- /dev/null
+extern void lib1_default (void);
+extern void lib2_default (void);
+
+int
+main (int argc __attribute__ ((unused)),
+ char** argv __attribute__ ((unused)))
+{
+ lib1_default ();
+ lib2_default ();
+ return 0;
+}
+
--- /dev/null
+#!/bin/sh
+
+# exclude_libs_test.sh -- test that library symbols are not exported.
+
+# Copyright 2009 Free Software Foundation, Inc.
+# Written by Doug Kwan <dougkwan@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 exclude_libs_test.c, a C source file
+# linked with option -Wl,--exclude-libs. We run readelf on
+# the resulting executable and check that symbols from two test library
+# archives are correctly hidden or left unmodified.
+
+check()
+{
+ file=$1
+ sym=$2
+ vis=$3
+
+ found=`grep " $sym\$" $file`
+ if test -z "$found"; then
+ echo "Symbol $sym not found."
+ exit 1
+ fi
+
+ match_vis=`grep " $sym\$" $file | grep " $vis "`
+ if test -z "$match_vis"; then
+ echo "Expected symbol $sym to have visibility $vis but found"
+ echo "$found"
+ exit 1
+ fi
+}
+
+check "exclude_libs_test.syms" "lib1_default" "HIDDEN"
+check "exclude_libs_test.syms" "lib1_protected" "HIDDEN"
+check "exclude_libs_test.syms" "lib1_internal" "INTERNAL"
+check "exclude_libs_test.syms" "lib1_hidden" "HIDDEN"
+check "exclude_libs_test.syms" "lib2_default" "DEFAULT"
+check "exclude_libs_test.syms" "lib2_protected" "PROTECTED"
+check "exclude_libs_test.syms" "lib2_internal" "INTERNAL"
+check "exclude_libs_test.syms" "lib2_hidden" "HIDDEN"
+
+exit 0
--- /dev/null
+void lib1_default (void);
+void lib1_hidden (void);
+void lib1_internal (void);
+void lib1_protected (void);
+
+void __attribute__((visibility ("default")))
+lib1_default (void)
+{
+}
+
+void __attribute__((visibility ("hidden")))
+lib1_hidden (void)
+{
+}
+
+void __attribute__((visibility ("internal")))
+lib1_internal (void)
+{
+}
+
+void __attribute__((visibility ("protected")))
+lib1_protected (void)
+{
+}
--- /dev/null
+void lib2_default (void);
+void lib2_hidden (void);
+void lib2_internal (void);
+void lib2_protected (void);
+
+void __attribute__((visibility ("default")))
+lib2_default (void)
+{
+}
+
+void __attribute__((visibility ("hidden")))
+lib2_hidden (void)
+{
+}
+
+void __attribute__((visibility ("internal")))
+lib2_internal (void)
+{
+}
+
+void __attribute__((visibility ("protected")))
+lib2_protected (void)
+{
+}