Don't use hidden visibility in libc.a with PIE on i386

Message ID 20170822224425.GA29673@gmail.com
State New, archived
Headers

Commit Message

Lu, Hongjiu Aug. 22, 2017, 10:44 p.m. UTC
  On i386, when multi-arch is enabled, all external functions must be
called via PIC PLT in PIE, which requires setting up EBX register,
since they may be IFUNC functions.

OK for master?

H.J.
---
	* config.h.in (NO_HIDDEN_EXTERN_FUNC_IN_PIE): New.
	* include/libc-symbols.h (__hidden_proto_hiddenattr): Add check
	for PIC and NO_HIDDEN_EXTERN_FUNC_IN_PIE.
	* sysdeps/i386/configure.ac (NO_HIDDEN_EXTERN_FUNC_IN_PIE): New
	AC_DEFINE if multi-arch is enabled.
	* sysdeps/i386/configure: Regenerated.
---
 config.h.in               | 4 ++++
 include/libc-symbols.h    | 1 +
 sysdeps/i386/configure    | 7 ++++++-
 sysdeps/i386/configure.ac | 6 ++++++
 4 files changed, 17 insertions(+), 1 deletion(-)
  

Comments

Florian Weimer Aug. 23, 2017, 9:23 a.m. UTC | #1
On 08/23/2017 12:44 AM, H.J. Lu wrote:
> On i386, when multi-arch is enabled, all external functions must be
> called via PIC PLT in PIE, which requires setting up EBX register,
> since they may be IFUNC functions.

This explanation does not make sense to me.  Could you elaborate further?

I suspect the actual problem is something else (probably a linker/ELF
limitation).

Thanks,
Florian
  
H.J. Lu Aug. 23, 2017, 12:25 p.m. UTC | #2
On Wed, Aug 23, 2017 at 2:23 AM, Florian Weimer <fweimer@redhat.com> wrote:
> On 08/23/2017 12:44 AM, H.J. Lu wrote:
>> On i386, when multi-arch is enabled, all external functions must be
>> called via PIC PLT in PIE, which requires setting up EBX register,
>> since they may be IFUNC functions.
>
> This explanation does not make sense to me.  Could you elaborate further?

When a linker detects a call to a hidden IFUNC function, foo, it will create a
PLT entry and resolve

call foo

to

call foo@PLT

On i386, there are 2 kinds of PLT:

1. PLT:

080499a0 <strstr@plt>:
 80499a0: ff 25 0c 00 22 08     jmp    *0x822000c
 80499a6: 68 00 00 00 00       push   $0x0
 80499ab: e9 e0 ff ff ff       jmp    8049990 <.plt>

This is used for non-PIC binary since the address of GOT is fixed.

2. PIC PLT:

000178a0 <_Unwind_Find_FDE@plt>:
   178a0: ff a3 0c 00 00 00     jmp    *0xc(%ebx)
   178a6: 68 00 00 00 00       push   $0x0
   178ab: e9 e0 ff ff ff       jmp    17890 <.plt>

This is used for PIC binary since the address of GOT is in EBX.

For non-PIC binary, it is OK to turn

call foo

to

call foo@PLT

since GOT is at the fixed address.  But for PIE, it isn't OK to
turn

call foo

to

call foo@PLT

since PIE uses PIC PLT and EBX doesn't have the address of
GOT.
  
H.J. Lu Oct. 5, 2017, 12:17 a.m. UTC | #3
On 8/23/17, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Wed, Aug 23, 2017 at 2:23 AM, Florian Weimer <fweimer@redhat.com> wrote:
>> On 08/23/2017 12:44 AM, H.J. Lu wrote:
>>> On i386, when multi-arch is enabled, all external functions must be
>>> called via PIC PLT in PIE, which requires setting up EBX register,
>>> since they may be IFUNC functions.
>>
>> This explanation does not make sense to me.  Could you elaborate further?
>
> When a linker detects a call to a hidden IFUNC function, foo, it will create
> a
> PLT entry and resolve
>
> call foo
>
> to
>
> call foo@PLT
>
> On i386, there are 2 kinds of PLT:
>
> 1. PLT:
>
> 080499a0 <strstr@plt>:
>  80499a0: ff 25 0c 00 22 08     jmp    *0x822000c
>  80499a6: 68 00 00 00 00       push   $0x0
>  80499ab: e9 e0 ff ff ff       jmp    8049990 <.plt>
>
> This is used for non-PIC binary since the address of GOT is fixed.
>
> 2. PIC PLT:
>
> 000178a0 <_Unwind_Find_FDE@plt>:
>    178a0: ff a3 0c 00 00 00     jmp    *0xc(%ebx)
>    178a6: 68 00 00 00 00       push   $0x0
>    178ab: e9 e0 ff ff ff       jmp    17890 <.plt>
>
> This is used for PIC binary since the address of GOT is in EBX.
>
> For non-PIC binary, it is OK to turn
>
> call foo
>
> to
>
> call foo@PLT
>
> since GOT is at the fixed address.  But for PIE, it isn't OK to
> turn
>
> call foo
>
> to
>
> call foo@PLT
>
> since PIE uses PIC PLT and EBX doesn't have the address of
> GOT.
>
>

I am checking it in.
  

Patch

diff --git a/config.h.in b/config.h.in
index 26ed7865ef..d397eeae1a 100644
--- a/config.h.in
+++ b/config.h.in
@@ -86,6 +86,10 @@ 
 #undef	PI_STATIC_AND_HIDDEN
 
 /* Define this to disable the 'hidden_proto' et al macros in
+   include/libc-symbols.h that avoid PLT slots in PIE.  */
+#undef  NO_HIDDEN_EXTERN_FUNC_IN_PIE
+
+/* Define this to disable the 'hidden_proto' et al macros in
    include/libc-symbols.h that avoid PLT slots in the shared objects.  */
 #undef	NO_HIDDEN
 
diff --git a/include/libc-symbols.h b/include/libc-symbols.h
index fe3571af52..4668902d5f 100644
--- a/include/libc-symbols.h
+++ b/include/libc-symbols.h
@@ -514,6 +514,7 @@  for linking")
 #else
 # ifndef __ASSEMBLER__
 #  if !defined SHARED && IS_IN (libc) && !defined LIBC_NONSHARED \
+      && (!defined PIC || !defined NO_HIDDEN_EXTERN_FUNC_IN_PIE) \
       && !defined NO_HIDDEN
 #   define __hidden_proto_hiddenattr(attrs...) \
   __attribute__ ((visibility ("hidden"), ##attrs))
diff --git a/sysdeps/i386/configure b/sysdeps/i386/configure
index 4d6685f7c2..4cf968d8bc 100644
--- a/sysdeps/i386/configure
+++ b/sysdeps/i386/configure
@@ -26,7 +26,7 @@  libc_compiler_builtin_inlined=no
 cat > conftest.c <<EOF
 int _start (void) { int a, b, c; __sync_val_compare_and_swap (&a, b, c); return 0; }
 EOF
-if ! { ac_try='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS
+if ! { ac_try='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS $no_ssp
 		     -O0 -nostdlib -nostartfiles
 		     -S conftest.c -o - | fgrep "__sync_val_compare_and_swap"
 		     1>&5'
@@ -79,3 +79,8 @@  fi
 
 $as_echo "#define PI_STATIC_AND_HIDDEN 1" >>confdefs.h
 
+
+if test x"$multi_arch" != xno; then
+  $as_echo "#define NO_HIDDEN_EXTERN_FUNC_IN_PIE 1" >>confdefs.h
+
+fi
diff --git a/sysdeps/i386/configure.ac b/sysdeps/i386/configure.ac
index f7766ad2b8..b598b120bc 100644
--- a/sysdeps/i386/configure.ac
+++ b/sysdeps/i386/configure.ac
@@ -48,3 +48,9 @@  fi
 dnl It is always possible to access static and hidden symbols in an
 dnl position independent way.
 AC_DEFINE(PI_STATIC_AND_HIDDEN)
+
+dnl When multi-arch is enabled, all external functions must be called
+dnl via PIC PLT in PIE, which requires setting up EBX register.
+if test x"$multi_arch" != xno; then
+  AC_DEFINE(NO_HIDDEN_EXTERN_FUNC_IN_PIE)
+fi