This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Add __canonicalize_funcptr_for_compare to libgcc on hppa-linux


This patch adds a libcall to canonicalize function pointers for TARGET_ELF32
(hppa-unknown-linux-gnu).  Currently, function pointers are not canonicalized
and, even in the same dynamic object, they can point to different function
descriptors.  This problem was noticed in the GNU ld testsuite where a
pointer to a function returned from a shared library didn't match the pointer
in the main.

The implementation is a bit of a kludge since nobody thought about this
problem when the dynamic loader interface to do lazy linking was conceived
for parisc-linux.  The 32-bit hpux port has the same problem but HP provides
a millicode function, $$sh_func_adrs, to do the job.

The core code needs to be extended to support the new libcall.

Tested on hppa-unknown-linux-gnu with no regressions.

Ok for main?

Dave
-- 
J. David Anglin                                  dave.anglin@nrc.ca
National Research Council of Canada              (613) 990-0752 (FAX: 952-6605)

2002-12-04  John David Anglin  <dave@hiauly1.hia.nrc.ca>

	* pa/fptr.c (__canonicalize_funcptr_for_compare): New file and function.
	* genemit.c (main): Include libfuncs.h in insn-output.c.
	* libfuncs.h (libfunc_index): Add LTI_canonicalize_funcptr_for_compare.
	(canonicalize_funcptr_for_compare_libfunc): Define.
	* pa.md (canonicalize_funcptr_for_compare): Output library call to
	canonicalize_funcptr_for_compare_libfunc on TARGET_ELF32.
	* pa32-linux.h (CANONICALIZE_FUNCPTR_FOR_COMPARE_LIBCALL,
	INIT_TARGET_OPTABS, CTOR_LIST_BEGIN): New defines.
	* pa/t-linux (LIB2FUNCS_EXTRA): New define.
	(fptr.c): Add rules.

--- /dev/null	2002-07-07 10:54:07.000000000 -0700
+++ config/pa/fptr.c	2002-12-03 20:12:37.000000000 -0800
@@ -0,0 +1,127 @@
+/* Subroutine for function pointer canonicalization on PA-RISC with ELF32.
+   Copyright 2002 Free Software Foundation, Inc.
+   Contributed by John David Anglin (dave.anglin@nrc.ca).
+
+This file is part of GNU CC.
+
+GNU CC 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 2, or (at your option)
+any later version.
+
+GNU CC 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 GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* WARNING: The code is this function depends on internal and undocumented
+   details of the GNU linker and dynamic loader as implemented for parisc
+   linux.  */
+
+/* This MUST match the defines sysdeps/hppa/dl-machine.h and
+   bfd/elf32-hppa.c.  */
+#define GOT_FROM_PLT_STUB (4*4)
+
+/* List of byte offsets in _dl_runtime_resolve to search for "bl" branches.
+   The first "bl" branch instruction found MUST be a call to fixup.  See
+   the define for TRAMPOLINE_TEMPLATE in sysdeps/hppa/dl-machine.h.  If
+   the trampoline template is changed, the list must be appropriately
+   updated.  The offset of -4 allows for a magic branch at the start of
+   the template should it be necessary to change the current branch
+   position.  */
+#define NOFFSETS 2
+static int fixup_branch_offset[NOFFSETS] = { 32, -4 };
+
+#define GET_FIELD(X, FROM, TO) \
+  ((X) >> (31 - (TO)) & ((1 << ((TO) - (FROM) + 1)) - 1))
+#define SIGN_EXTEND(VAL,BITS) \
+  ((int) ((VAL) >> ((BITS) - 1) ? (-1 << (BITS)) | (VAL) : (VAL)))
+
+struct link_map;
+typedef int (*fptr_t) (void);
+typedef int (*fixup_t) (struct link_map *, unsigned int);
+extern unsigned int _GLOBAL_OFFSET_TABLE_;
+
+/* __canonicalize_funcptr_for_compare must be hidden so that it is not
+   placed in the dynamic symbol table.  Like millicode functions, it
+   must be linked into all binaries in order access the got table of 
+   that binary.  However, we don't use the millicode calling convention
+   and the routine must be a normal function so that it can be compiled
+   as pic code.  */
+unsigned int __canonicalize_funcptr_for_compare (fptr_t)
+      __attribute__ ((visibility ("hidden")));
+
+unsigned int
+__canonicalize_funcptr_for_compare (fptr)
+     fptr_t fptr;
+{
+  static unsigned int fixup_plabel[2];
+  static fixup_t fixup;
+  unsigned int *plabel, *got;
+
+  /* -1 is special.  It is used in crtend to mark the end of a list of
+     function pointers.  Also return immediately if the plabel bit is
+     not set in the function pointer.  In this case, the function pointer
+     points directly to the function.  */
+  if ((int) fptr == -1 || !((int) fptr & 2))
+    return (unsigned int) fptr;
+
+  /* The function pointer points to a function descriptor (plabel).  If
+     the plabel hasn't been resolved, the first word of the plabel points
+     to the entry of the PLT stub just before the global offset table.
+     The second word in the plabel contains the relocation offset for the
+     function.  */
+  plabel = (unsigned int *) ((unsigned int) fptr & ~3);
+  got = (unsigned int *) (plabel[0] + GOT_FROM_PLT_STUB);
+
+  /* Return the address of the function if the plabel has been resolved.  */
+  if (got !=  &_GLOBAL_OFFSET_TABLE_)
+    return plabel[0];
+
+  /* Initialize our plabel for calling fixup if we haven't done so already.
+     This code needs to be thread safe but we don't have to be too careful
+     as the result is invariant.  */
+  if (!fixup)
+    {
+      int i;
+      unsigned int *iptr;
+
+      /* Find the first "bl" branch in the offset search list.  This is a
+	 call to fixup or a magic branch to fixup at the beginning of the
+	 trampoline template.  The fixup function does the actual runtime
+	 resolution of function decriptors.  We only look for "bl" branches
+	 with a 17-bit pc-relative displacement.  */
+      for (i = 0; i < NOFFSETS; i++)
+	{
+	  iptr = (unsigned int *) (got[-2] + fixup_branch_offset[i]);
+	  if ((*iptr & 0xfc00e000) == 0xe8000000)
+	    break;
+	}
+
+      /* This should not happen... */
+      if (i == NOFFSETS)
+	return ~0;
+
+      /* Extract the 17-bit displacement from the instruction.  */
+      iptr += SIGN_EXTEND (GET_FIELD (*iptr, 19, 28) |
+			   GET_FIELD (*iptr, 29, 29) << 10 |
+			   GET_FIELD (*iptr, 11, 15) << 11 |
+			   GET_FIELD (*iptr, 31, 31) << 16, 17);
+
+      /* Build a plabel for an indirect call to fixup.  */
+      fixup_plabel[0] = (unsigned int) iptr + 8;  /* address of fixup */
+      fixup_plabel[1] = got[-1];		  /* ltp for fixup */
+      fixup = (fixup_t) ((int) fixup_plabel | 3);
+    }
+
+  /* Call fixup to resolve the function address.  got[1] contains the
+     link_map pointer and plabel[1] the relocation offset.  */
+  fixup ((struct link_map *) got[1], plabel[1]);
+
+  return plabel[0];
+}
Index: genemit.c
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/genemit.c,v
retrieving revision 1.77
diff -u -3 -p -r1.77 genemit.c
--- genemit.c	30 Jul 2002 21:57:56 -0000	1.77
+++ genemit.c	4 Dec 2002 02:59:16 -0000
@@ -830,6 +830,7 @@ from the machine description file `md'. 
   printf ("#include \"tm_p.h\"\n");
   printf ("#include \"function.h\"\n");
   printf ("#include \"expr.h\"\n");
+  printf ("#include \"libfuncs.h\"\n");
   printf ("#include \"optabs.h\"\n");
   printf ("#include \"real.h\"\n");
   printf ("#include \"flags.h\"\n");
Index: libfuncs.h
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/libfuncs.h,v
retrieving revision 1.5
diff -u -3 -p -r1.5 libfuncs.h
--- libfuncs.h	4 Jun 2002 07:07:50 -0000	1.5
+++ libfuncs.h	4 Dec 2002 02:59:16 -0000
@@ -143,6 +143,8 @@ enum libfunc_index
   LTI_profile_function_entry,
   LTI_profile_function_exit,
 
+  LTI_canonicalize_funcptr_for_compare,
+
   LTI_MAX
 };
 
@@ -270,5 +272,7 @@ extern GTY(()) rtx libfunc_table[LTI_MAX
 
 #define profile_function_entry_libfunc	(libfunc_table[LTI_profile_function_entry])
 #define profile_function_exit_libfunc	(libfunc_table[LTI_profile_function_exit])
+
+#define canonicalize_funcptr_for_compare_libfunc	(libfunc_table[LTI_canonicalize_funcptr_for_compare])
 
 #endif /* GCC_LIBFUNCS_H */
Index: config/pa/pa.md
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/config/pa/pa.md,v
retrieving revision 1.115
diff -u -3 -p -r1.115 pa.md
--- config/pa/pa.md	27 Nov 2002 02:34:15 -0000	1.115
+++ config/pa/pa.md	4 Dec 2002 02:59:17 -0000
@@ -7277,9 +7277,17 @@
 	      (clobber (reg:SI 31))])
    (set (match_operand:SI 0 "register_operand" "")
 	(reg:SI 29))]
-  "! TARGET_PORTABLE_RUNTIME && !TARGET_64BIT && !TARGET_ELF32"
+  "!TARGET_PORTABLE_RUNTIME && !TARGET_64BIT"
   "
 {
+  if (TARGET_ELF32)
+    {
+      emit_library_call_value (canonicalize_funcptr_for_compare_libfunc,
+      			       operands[0], LCT_NORMAL, Pmode,
+			       1, operands[1], Pmode);
+      DONE;
+    }
+
   operands[2] = gen_reg_rtx (SImode);
   if (GET_CODE (operands[1]) != REG)
     {
Index: config/pa/pa32-linux.h
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/config/pa/pa32-linux.h,v
retrieving revision 1.8
diff -u -3 -p -r1.8 pa32-linux.h
--- config/pa/pa32-linux.h	3 Oct 2002 04:05:54 -0000	1.8
+++ config/pa/pa32-linux.h	4 Dec 2002 02:59:17 -0000
@@ -29,3 +29,28 @@ Boston, MA 02111-1307, USA.  */
    subspace stubs, so we allow sibcalls to all functions.  */
 #undef FUNCTION_OK_FOR_SIBCALL
 #define FUNCTION_OK_FOR_SIBCALL(DECL) 1
+
+/* We need a LIBCALL to canonicalize function pointers because of the
+   way function pointers are handled when doing lazy linking.  */
+#define CANONICALIZE_FUNCPTR_FOR_COMPARE_LIBCALL \
+  "__canonicalize_funcptr_for_compare"
+
+#define INIT_TARGET_OPTABS						\
+  do {									\
+    canonicalize_funcptr_for_compare_libfunc				\
+      = init_one_libfunc (CANONICALIZE_FUNCPTR_FOR_COMPARE_LIBCALL);	\
+  } while (0)
+
+/* __canonicalize_funcptr_for_compare is referenced in crtend.o
+   and the reference isn't resolved in objects that don't compare
+   function pointers.  Thus, we need to play games to provide a
+   reference in crtbegin.o.  The rest of the define is the same
+   as in crtstuff.c  */
+#define CTOR_LIST_BEGIN \
+  asm (".type __canonicalize_funcptr_for_compare,@function\n"		\
+"	.text\n"							\
+"	.word __canonicalize_funcptr_for_compare-$PIC_pcrel$0");	\
+  STATIC func_ptr __CTOR_LIST__[1]					\
+    __attribute__ ((__unused__, section(".ctors"),			\
+		    aligned(sizeof(func_ptr))))				\
+    = { (func_ptr) (-1) }
Index: config/pa/t-linux
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/config/pa/t-linux,v
retrieving revision 1.7
diff -u -3 -p -r1.7 t-linux
--- config/pa/t-linux	30 Apr 2002 19:47:38 -0000	1.7
+++ config/pa/t-linux	4 Dec 2002 02:59:17 -0000
@@ -5,8 +5,14 @@
 LIB1ASMFUNCS = _divI _divU _remI _remU _div_const _mulI _dyncall
 LIB1ASMSRC = pa/milli64.S
 
-# Compile crtbeginS.o and crtendS.o as PIC.
-CRTSTUFF_T_CFLAGS_S = -fPIC
-
 # Compile libgcc2.a as PIC.
 TARGET_LIBGCC2_CFLAGS = -fPIC -DELF=1 -DLINUX=1
+
+LIB2FUNCS_EXTRA=fptr.c
+
+fptr.c: $(srcdir)/config/pa/fptr.c
+	rm -f fptr.c
+	cp $(srcdir)/config/pa/fptr.c .
+
+# Compile crtbeginS.o and crtendS.o as PIC.
+CRTSTUFF_T_CFLAGS_S = -fPIC


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]