Don't allow COPY relocations for protected symbols.

2016-05-19  Cary Coutant  <ccoutant@gmail.com>

gold/
	PR gold/19823
	* copy-relocs.cc (Copy_relocs::make_copy_reloc): Add object
	parameter; check for protected symbol.
	* copy-relocs.h (Copy_relocs::make_copy_reloc): Add object parameter.
	* mips.cc (Mips_copy_relocs): Adjust call to make_copy_reloc.
	* symtab.cc (Symbol::init_fields): Initialize is_protected_.
	(Symbol_table::add_from_dynobj): Mark protected symbols.
	* symtab.h (Symbol::is_protected): New method.
	(Symbol::set_is_protected): New method.
	(Symbol::is_protected_): New data member.

	* testsuite/Makefile.am (copy_test_protected): New test.
	* testsuite/Makefile.in: Regenerate.
	* testsuite/copy_test.cc (main): Add legal reference to protected
	symbol.
	* testsuite/copy_test_v1.cc (main): Likewise.
	* testsuite/copy_test_2.cc (ip): Add protected symbol.
	* testsuite/copy_test_protected.cc: New test source file.
	* testsuite/copy_test_protected.sh: New test script.


diff --git a/gold/copy-relocs.cc b/gold/copy-relocs.cc
index 32288c8..ce019c4 100644
--- a/gold/copy-relocs.cc
+++ b/gold/copy-relocs.cc
@@ -48,7 +48,7 @@ Copy_relocs<sh_type, size, big_endian>::copy_reloc(
     Output_data_reloc<sh_type, true, size, big_endian>* reloc_section)
 {
   if (this->need_copy_reloc(sym, object, shndx))
-    this->make_copy_reloc(symtab, layout, sym, reloc_section);
+    this->make_copy_reloc(symtab, layout, sym, object, reloc_section);
   else
     {
       // We may not need a COPY relocation.  Save this relocation to
@@ -111,11 +111,24 @@ Copy_relocs<sh_type, size, big_endian>::make_copy_reloc(
     Symbol_table* symtab,
     Layout* layout,
     Sized_symbol<size>* sym,
+    Sized_relobj_file<size, big_endian>* object,
     Output_data_reloc<sh_type, true, size, big_endian>* reloc_section)
 {
   // We should not be here if -z nocopyreloc is given.
   gold_assert(parameters->options().copyreloc());
 
+  gold_assert(sym->is_from_dynobj());
+
+  // The symbol must not have protected visibility.
+  if (sym->is_protected())
+    {
+      gold_error(_("%s: cannot make copy relocation for "
+		   "protected symbol '%s', defined in %s"),
+		 object->name().c_str(),
+		 sym->name(),
+		 sym->object()->name().c_str());
+    }
+
   typename elfcpp::Elf_types<size>::Elf_WXword symsize = sym->symsize();
 
   // There is no defined way to determine the required alignment of
@@ -124,7 +137,6 @@ Copy_relocs<sh_type, size, big_endian>::make_copy_reloc(
   // is defined; presumably we do not require an alignment larger than
   // that.  Then we reduce that alignment if the symbol is not aligned
   // within the section.
-  gold_assert(sym->is_from_dynobj());
   bool is_ordinary;
   unsigned int shndx = sym->shndx(&is_ordinary);
   gold_assert(is_ordinary);
diff --git a/gold/copy-relocs.h b/gold/copy-relocs.h
index 68ec5c0..aa42c7c 100644
--- a/gold/copy-relocs.h
+++ b/gold/copy-relocs.h
@@ -123,6 +123,7 @@ class Copy_relocs
   // Make a new COPY reloc and emit it.
   void
   make_copy_reloc(Symbol_table*, Layout*, Sized_symbol<size>*,
+		  Sized_relobj_file<size, big_endian>* object,
 		  Output_data_reloc<sh_type, true, size, big_endian>*);
 
   // A list of relocs to be saved.
diff --git a/gold/mips.cc b/gold/mips.cc
index ffaad45..e363f3e 100644
--- a/gold/mips.cc
+++ b/gold/mips.cc
@@ -7750,6 +7750,7 @@ Mips_copy_relocs<sh_type, size, big_endian>::emit_entry(
   else
     this->make_copy_reloc(symtab, layout,
                           static_cast<Sized_symbol<size>*>(entry.sym_),
+                          entry.relobj_,
                           reloc_section);
 }
 
diff --git a/gold/symtab.cc b/gold/symtab.cc
index a7edbb1..5ce5c31 100644
--- a/gold/symtab.cc
+++ b/gold/symtab.cc
@@ -80,6 +80,7 @@ Symbol::init_fields(const char* name, const char* version,
   this->undef_binding_set_ = false;
   this->undef_binding_weak_ = false;
   this->is_predefined_ = false;
+  this->is_protected_ = false;
 }
 
 // Return the demangled version of the symbol's name, but only
@@ -1610,6 +1611,13 @@ Symbol_table::add_from_dynobj(
 	  && res->object() == dynobj)
 	object_symbols.push_back(res);
 
+      // If the symbol has protected visibility in the dynobj,
+      // mark it as such if it was not overridden.
+      if (res->source() == Symbol::FROM_OBJECT
+          && res->object() == dynobj
+          && sym.get_st_visibility() == elfcpp::STV_PROTECTED)
+        res->set_is_protected();
+
       if (sympointers != NULL)
 	(*sympointers)[i] = res;
     }
diff --git a/gold/symtab.h b/gold/symtab.h
index 5cee458..b26b4e0 100644
--- a/gold/symtab.h
+++ b/gold/symtab.h
@@ -870,6 +870,19 @@ class Symbol
   is_cxx_vtable() const
   { return is_prefix_of("_ZTV", this->name_); }
 
+  // Return true if this symbol is protected in a shared object.
+  // This is not the same as checking if visibility() == elfcpp::STV_PROTECTED,
+  // because the visibility_ field reflects the symbol's visibility from
+  // outside the shared object.
+  bool
+  is_protected() const
+  { return this->is_protected_; }
+
+  // Mark this symbol as protected in a shared object.
+  void
+  set_is_protected()
+  { this->is_protected_ = true; }
+
  protected:
   // Instances of this class should always be created at a specific
   // size.
@@ -1067,6 +1080,10 @@ class Symbol
   bool undef_binding_weak_ : 1;
   // True if this symbol is a predefined linker symbol (bit 34).
   bool is_predefined_ : 1;
+  // True if this symbol has protected visibility in a shared object (bit 35).
+  // The visibility_ field will be STV_DEFAULT in this case because we
+  // must treat it as such from outside the shared object.
+  bool is_protected_  : 1;
 };
 
 // The parts of a symbol which are size specific.  Using a template
diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am
index bf222c3..01cae9f 100644
--- a/gold/testsuite/Makefile.am
+++ b/gold/testsuite/Makefile.am
@@ -848,6 +848,18 @@ copy_test_2_pic.o: copy_test_2.cc
 copy_test_2.so: gcctestdir/ld copy_test_2_pic.o
 	$(CXXLINK) -Bgcctestdir/ -shared copy_test_2_pic.o
 
+check_SCRIPTS += copy_test_protected.sh
+check_DATA += copy_test_protected.err
+MOSTLYCLEANFILES += copy_test_protected.err
+copy_test_protected.err: copy_test_protected.o copy_test_2.so gcctestdir/ld
+	@echo $(CXXLINK) -Bgcctestdir/ -o copy_test_protected copy_test_protected.o copy_test_2.so -Wl,-R,. "2>$@"
+	@if $(CXXLINK) -Bgcctestdir/ -o copy_test_protected copy_test_protected.o copy_test_2.so -Wl,-R,. 2>$@; \
+	then \
+	  echo 1>&2 "Link of copy_test_protected should have failed"; \
+	  rm -f $@; \
+	  exit 1; \
+	fi
+
 if TLS
 
 check_PROGRAMS += tls_test
diff --git a/gold/testsuite/copy_test.cc b/gold/testsuite/copy_test.cc
index 3ee8af9..1d843c1 100644
--- a/gold/testsuite/copy_test.cc
+++ b/gold/testsuite/copy_test.cc
@@ -31,6 +31,9 @@ extern char b;
 
 // From copy_test_2.cc.
 extern long long l;
+extern int ip; // protected visibility; may not be copied
+
+int* ipp = &ip;
 
 int
 main()
@@ -39,5 +42,6 @@ main()
   assert(b == 1);
   assert(l == 2);
   assert((reinterpret_cast<uintptr_t>(&l) & 0x7) == 0);
+  assert(*ipp == 3);
   return 0;
 }
diff --git a/gold/testsuite/copy_test_2.cc b/gold/testsuite/copy_test_2.cc
index 08e6007..ff2847e 100644
--- a/gold/testsuite/copy_test_2.cc
+++ b/gold/testsuite/copy_test_2.cc
@@ -21,3 +21,5 @@
 // MA 02110-1301, USA.
 
 long long l = 2;
+
+int ip __attribute__((visibility("protected"))) = 3;
diff --git a/gold/testsuite/copy_test_protected.cc b/gold/testsuite/copy_test_protected.cc
new file mode 100644
index 0000000..5e26834
--- /dev/null
+++ b/gold/testsuite/copy_test_protected.cc
@@ -0,0 +1,36 @@
+// copy_test_protected.cc -- test copy relocs for gold
+
+// Copyright (C) 2016 Free Software Foundation, Inc.
+// Written by Cary Coutant <ccoutant@gmail.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.
+
+#include <cassert>
+#include <stdint.h>
+
+// From copy_test_2.cc.
+extern int ip; // protected visibility; may not be copied
+
+int
+main()
+{
+  // This should produce a link-time error because we cannot
+  // create a copy relocation to a protected symbol.
+  assert(ip == 3);
+  return 0;
+}
diff --git a/gold/testsuite/copy_test_protected.sh b/gold/testsuite/copy_test_protected.sh
new file mode 100755
index 0000000..e217959
--- /dev/null
+++ b/gold/testsuite/copy_test_protected.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+# copy_test_protected.sh -- a test case for copy relocations.
+
+# Copyright (C) 2016 Free Software Foundation, Inc.
+# Written by Cary Coutant <ccoutant@gmail.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.
+
+check()
+{
+    if ! grep -q "$2" "$1"
+    then
+	echo "Did not find expected error in $1:"
+	echo "   $2"
+	echo ""
+	echo "Actual error output below:"
+	cat "$1"
+	exit 1
+    fi
+}
+
+check copy_test_protected.err "protected symbol .ip."
+
+exit 0
diff --git a/gold/testsuite/copy_test_v1.cc b/gold/testsuite/copy_test_v1.cc
index cee509c..a18e303 100644
--- a/gold/testsuite/copy_test_v1.cc
+++ b/gold/testsuite/copy_test_v1.cc
@@ -35,6 +35,9 @@ extern char b;
 
 // From copy_test_2.cc.
 extern long long l;
+extern int ip; // protected visibility; may not be copied
+
+int* ipp = &ip;
 
 int
 main()
@@ -43,5 +46,6 @@ main()
   assert(b == 1);
   assert(l == 3);	// Deliberately incorrect.
   assert((reinterpret_cast<uintptr_t>(&l) & 0x7) == 0);
+  assert(*ipp == 3);
   return 0;
 }
