+2011-07-12  Ian Lance Taylor  <iant@google.com>
+
+       PR gold/12980
+       * i386.cc (Target_i386::Scan::global): For a GOT reloc, use a
+       GLOB_DAT relocation rather than a RELATIVE relocation for a
+       protected symbol when creating a shared library.
+       * x86_64.cc (Target_x86_64::Scan::global): Likewise.
+       * testsuite/protected_1.cc (f2, get_f2_addr): New functions.
+       * testsuite/protected_main_1.cc (main): Test that protected
+       function has same address.
+
 2011-07-11  Ian Lance Taylor  <iant@google.com>
 
        PR gold/12979
 
             // If this symbol is not fully resolved, we need to add a
             // GOT entry with a dynamic relocation.
             Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+
+           // Use a GLOB_DAT rather than a RELATIVE reloc if:
+           //
+           // 1) The symbol may be defined in some other module.
+           //
+           // 2) We are building a shared library and this is a
+           // protected symbol; using GLOB_DAT means that the dynamic
+           // linker can use the address of the PLT in the main
+           // executable when appropriate so that function address
+           // comparisons work.
+           //
+           // 3) This is a STT_GNU_IFUNC symbol in position dependent
+           // code, again so that function address comparisons work.
             if (gsym->is_from_dynobj()
                 || gsym->is_undefined()
                 || gsym->is_preemptible()
+               || (gsym->visibility() == elfcpp::STV_PROTECTED
+                   && parameters->options().shared())
                || (gsym->type() == elfcpp::STT_GNU_IFUNC
                    && parameters->options().output_is_position_independent()))
               got->add_global_with_rel(gsym, GOT_TYPE_STANDARD,
 
 {
   return 1;
 }
+
+// The function f2 is used to test that the executable can see the
+// same function address for a protected function in the executable
+// and in the shared library.  We can't use the visibility attribute
+// here, becaues that may cause gcc to generate a PC relative reloc;
+// we need it to get the value from the GOT.  I'm not sure this is
+// really useful, given that it doesn't work with the visibility
+// attribute.  This test exists here mainly because the glibc
+// testsuite has the same test, and we want to make sure that gold
+// passes the glibc testsuite.
+
+extern "C" int f2();
+asm(".protected f2");
+
+extern "C" int
+f2()
+{
+  return 2;
+}
+
+int
+(*get_f2_addr())()
+{
+  return f2;
+}
 
 extern bool t1();
 extern bool t2();
 
+extern "C" int f2();
+extern int (*get_f2_addr()) ();
+
 int
 main()
 {
   assert(t1());
   assert(t2());
+  assert(&f2 == get_f2_addr());
 }
 
             // If this symbol is not fully resolved, we need to add a
             // dynamic relocation for it.
             Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+
+           // Use a GLOB_DAT rather than a RELATIVE reloc if:
+           //
+           // 1) The symbol may be defined in some other module.
+           //
+           // 2) We are building a shared library and this is a
+           // protected symbol; using GLOB_DAT means that the dynamic
+           // linker can use the address of the PLT in the main
+           // executable when appropriate so that function address
+           // comparisons work.
+           //
+           // 3) This is a STT_GNU_IFUNC symbol in position dependent
+           // code, again so that function address comparisons work.
            if (gsym->is_from_dynobj()
                || gsym->is_undefined()
                || gsym->is_preemptible()
+               || (gsym->visibility() == elfcpp::STV_PROTECTED
+                   && parameters->options().shared())
                || (gsym->type() == elfcpp::STT_GNU_IFUNC
                    && parameters->options().output_is_position_independent()))
               got->add_global_with_rela(gsym, GOT_TYPE_STANDARD, rela_dyn,