This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Add __canonicalize_funcptr_for_compare to libgcc on hppa-linux
- From: "John David Anglin" <dave at hiauly1 dot hia dot nrc dot ca>
- To: gcc-patches at gcc dot gnu dot org
- Cc: rth at redhat dot com, law at redhat dot com
- Date: Wed, 4 Dec 2002 13:03:42 -0500 (EST)
- Subject: 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