[3/3] libctf: look for a qsort_r that functions properly

Message ID 20190603204735.203510-3-nick.alcock@oracle.com
State New
Headers show
Series
  • [1/3] libctf: fix the type of ctf_enum.cte_value
Related show

Commit Message

Nick Alcock June 3, 2019, 8:47 p.m.
We cannot just look for any declaration of qsort_r, because some
operating systems have a qsort_r that has a different prototype
but which still has a pair of pointers in the right places (the last two
args are interchanged), so we cannot rely on compilation errors to flag
up a problem.

So, instead, use AC_RUN_IFELSE to check for the function of qsort_r(),
passing function pointers in to both args and making sure that we never
actually dereference the arg pointer.

This may still be problematic on systems with different representations
for function pointers and integer pointers, but even then we should get
the right result from configure (there just might be an ugly coredump
too).

(Now we are not using AC_LIBOBJ any more, we can use a better name for
the qsort_r replacement as well.)

libctf/
	* qsort_r.c: Rename to...
	* ctf-qsort_r.c: ... this.
	(_quicksort): Define to ctf_qsort_r.
	* ctf-decls.h (qsort_r): Remove.
	(ctf_qsort_r): Add.
	* Makefile.am (libctf_a_LIBADD): Remove.
	(libctf_a_SOURCES): New, add ctf-qsort_r.c.
	* ctf-archive.c (ctf_arc_write): Call ctf_qsort_r, not qsort_r.
	* ctf-create.c (ctf_update): Likewise.
	* configure.ac: Check for specific qsort_r that functions properly,
	not just for any declaration of it.
	* Makefile.in: Regenerate.
	* config.h.in: Likewise.
	* configure: Likewise.
---
 libctf/Makefile.am                  |   4 +-
 libctf/Makefile.in                  |  29 ++++---
 libctf/config.h.in                  |   7 +-
 libctf/configure                    | 115 +++++++++++++++-------------
 libctf/configure.ac                 |  30 +++++++-
 libctf/ctf-archive.c                |   9 ++-
 libctf/ctf-create.c                 |   2 +-
 libctf/ctf-decls.h                  |  16 +++-
 libctf/{qsort_r.c => ctf-qsort_r.c} |   2 +-
 9 files changed, 134 insertions(+), 80 deletions(-)
 rename libctf/{qsort_r.c => ctf-qsort_r.c} (99%)

-- 
2.21.0.237.gd0cfaa883d

Comments

Florian Weimer June 4, 2019, 9:55 a.m. | #1
* Nick Alcock:

> +AC_CACHE_CHECK([for qsort_r with arg last], ac_cv_libctf_qsort_r_arg_last,

> +[AC_RUN_IFELSE(

> +  [AC_LANG_PROGRAM(

> +  [#include <stdlib.h>

> +  int do_not_call (const void *a, const void *b, void *arg)

> +  {

> +    return (a < b);

> +  }

> +  static int works = 0;

> +  int conftest_compar (const void *a, const void *b, void *arg)

> +  {

> +    if (arg == do_not_call)

> +      works = 1;

> +    return (a < b);

> +  }

> +  int foo[[2]] = { 0, 1 };],

> +  [qsort_r (foo, 2, sizeof (int), conftest_compar, do_not_call);

> +   return (works == 0);])],

> +  [ac_cv_libctf_qsort_r_arg_last=yes],

> +  [ac_cv_libctf_qsort_r_arg_last=no],

> +  [ac_cv_libctf_qsort_r_arg_last=no])])


Can you use a compile test with the expected declaration, trying to
compile this?

#undef qsort_r
#include <stdlib.h>
void qsort_r (void *, size_t, size_t,
              int (*) (const void *, const void *, void *),
              void *);

And:

#undef qsort_r
#include <stdlib.h>
void qsort_r (void *, size_t, size_t, void *,
              int (*) (const void *, const void *, void *));

This will result in a hard compiler error, not just in an error.

Thanks,
Florian
Florian Weimer June 4, 2019, 9:58 a.m. | #2
* Florian Weimer:

> * Nick Alcock:

>

>> +AC_CACHE_CHECK([for qsort_r with arg last], ac_cv_libctf_qsort_r_arg_last,

>> +[AC_RUN_IFELSE(

>> +  [AC_LANG_PROGRAM(

>> +  [#include <stdlib.h>

>> +  int do_not_call (const void *a, const void *b, void *arg)

>> +  {

>> +    return (a < b);

>> +  }

>> +  static int works = 0;

>> +  int conftest_compar (const void *a, const void *b, void *arg)

>> +  {

>> +    if (arg == do_not_call)

>> +      works = 1;

>> +    return (a < b);

>> +  }

>> +  int foo[[2]] = { 0, 1 };],

>> +  [qsort_r (foo, 2, sizeof (int), conftest_compar, do_not_call);

>> +   return (works == 0);])],

>> +  [ac_cv_libctf_qsort_r_arg_last=yes],

>> +  [ac_cv_libctf_qsort_r_arg_last=no],

>> +  [ac_cv_libctf_qsort_r_arg_last=no])])

>

> Can you use a compile test with the expected declaration, trying to

> compile this?

>

> #undef qsort_r

> #include <stdlib.h>

> void qsort_r (void *, size_t, size_t,

>               int (*) (const void *, const void *, void *),

>               void *);

>

> And:

>

> #undef qsort_r

> #include <stdlib.h>

> void qsort_r (void *, size_t, size_t, void *,

>               int (*) (const void *, const void *, void *));

>

> This will result in a hard compiler error, not just in an error.


(I meant not just in a warning.)

Florian
Pedro Alves June 5, 2019, 10:25 a.m. | #3
On 6/3/19 9:47 PM, Nick Alcock wrote:

> We cannot just look for any declaration of qsort_r, because some

> operating systems


OOC, could you give some examples?

> have a qsort_r that has a different prototype

> but which still has a pair of pointers in the right places (the last two

> args are interchanged), so we cannot rely on compilation errors to flag

> up a problem.


> 

> So, instead, use AC_RUN_IFELSE to check for the function of qsort_r(),

> passing function pointers in to both args and making sure that we never

> actually dereference the arg pointer.


AC_RUN_IFELSE is really best avoided; obviously you can't run when
cross compiling.

How does gnulib handle this?

Thanks,
Pedro Alves
Nick Alcock June 5, 2019, 10:43 a.m. | #4
On 5 Jun 2019, Pedro Alves told this:

> On 6/3/19 9:47 PM, Nick Alcock wrote:

>

>> We cannot just look for any declaration of qsort_r, because some

>> operating systems

>

> OOC, could you give some examples?


This is a BSD-heritage thing, so MacOS X and FreeBSD are examples.

>> So, instead, use AC_RUN_IFELSE to check for the function of qsort_r(),

>> passing function pointers in to both args and making sure that we never

>> actually dereference the arg pointer.

>

> AC_RUN_IFELSE is really best avoided; obviously you can't run when

> cross compiling.

>

> How does gnulib handle this?


This is an old patch: v2, posted later, uses gnulib's method (and mostly
gnulib's code too).

-- 
NULL && (void)

Patch

diff --git a/libctf/Makefile.am b/libctf/Makefile.am
index 49c9f5280a..926c9919c5 100644
--- a/libctf/Makefile.am
+++ b/libctf/Makefile.am
@@ -35,4 +35,6 @@  noinst_LIBRARIES = libctf.a
 libctf_a_SOURCES = ctf-archive.c ctf-dump.c ctf-create.c ctf-decl.c ctf-error.c \
 		   ctf-hash.c ctf-labels.c ctf-lookup.c ctf-open.c ctf-open-bfd.c \
 		   ctf-subr.c ctf-types.c ctf-util.c
-libctf_a_LIBADD = $(LIBOBJS)
+if NEED_CTF_QSORT_R
+libctf_a_SOURCES += ctf-qsort_r.c
+endif
diff --git a/libctf/Makefile.in b/libctf/Makefile.in
index c2cada6616..4fea156c44 100644
--- a/libctf/Makefile.in
+++ b/libctf/Makefile.in
@@ -104,6 +104,7 @@  POST_INSTALL = :
 NORMAL_UNINSTALL = :
 PRE_UNINSTALL = :
 POST_UNINSTALL = :
+@NEED_CTF_QSORT_R_TRUE@am__append_1 = ctf-qsort_r.c
 subdir = .
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/../config/depstand.m4 \
@@ -128,12 +129,17 @@  am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@)
 am__v_AR_0 = @echo "  AR      " $@;
 am__v_AR_1 = 
 libctf_a_AR = $(AR) $(ARFLAGS)
-libctf_a_DEPENDENCIES = $(LIBOBJS)
+libctf_a_LIBADD =
+am__libctf_a_SOURCES_DIST = ctf-archive.c ctf-dump.c ctf-create.c \
+	ctf-decl.c ctf-error.c ctf-hash.c ctf-labels.c ctf-lookup.c \
+	ctf-open.c ctf-open-bfd.c ctf-subr.c ctf-types.c ctf-util.c \
+	ctf-qsort_r.c
+@NEED_CTF_QSORT_R_TRUE@am__objects_1 = ctf-qsort_r.$(OBJEXT)
 am_libctf_a_OBJECTS = ctf-archive.$(OBJEXT) ctf-dump.$(OBJEXT) \
 	ctf-create.$(OBJEXT) ctf-decl.$(OBJEXT) ctf-error.$(OBJEXT) \
 	ctf-hash.$(OBJEXT) ctf-labels.$(OBJEXT) ctf-lookup.$(OBJEXT) \
 	ctf-open.$(OBJEXT) ctf-open-bfd.$(OBJEXT) ctf-subr.$(OBJEXT) \
-	ctf-types.$(OBJEXT) ctf-util.$(OBJEXT)
+	ctf-types.$(OBJEXT) ctf-util.$(OBJEXT) $(am__objects_1)
 libctf_a_OBJECTS = $(am_libctf_a_OBJECTS)
 AM_V_P = $(am__v_P_@AM_V@)
 am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
@@ -164,7 +170,7 @@  am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
 am__v_CCLD_0 = @echo "  CCLD    " $@;
 am__v_CCLD_1 = 
 SOURCES = $(libctf_a_SOURCES)
-DIST_SOURCES = $(libctf_a_SOURCES)
+DIST_SOURCES = $(am__libctf_a_SOURCES_DIST)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
@@ -196,7 +202,7 @@  am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \
 	$(top_srcdir)/../ar-lib $(top_srcdir)/../compile \
 	$(top_srcdir)/../depcomp $(top_srcdir)/../install-sh \
 	$(top_srcdir)/../missing $(top_srcdir)/../mkinstalldirs \
-	ChangeLog qsort_r.c
+	ChangeLog
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 distdir = $(PACKAGE)-$(VERSION)
 top_distdir = $(distdir)
@@ -323,11 +329,10 @@  ZLIBINC = @zlibinc@
 AM_CPPFLAGS = -D_GNU_SOURCE -I$(top_srcdir) -I$(top_srcdir)/../include -I$(top_srcdir)/../bfd -I../bfd
 AM_CFLAGS = -std=gnu99 @ac_libctf_warn_cflags@ @warn@ @c_warn@ @WARN_PEDANTIC@ @WERROR@ $(ZLIBINC)
 noinst_LIBRARIES = libctf.a
-libctf_a_SOURCES = ctf-archive.c ctf-dump.c ctf-create.c ctf-decl.c ctf-error.c \
-		   ctf-hash.c ctf-labels.c ctf-lookup.c ctf-open.c ctf-open-bfd.c \
-		   ctf-subr.c ctf-types.c ctf-util.c
-
-libctf_a_LIBADD = $(LIBOBJS)
+libctf_a_SOURCES = ctf-archive.c ctf-dump.c ctf-create.c ctf-decl.c \
+	ctf-error.c ctf-hash.c ctf-labels.c ctf-lookup.c ctf-open.c \
+	ctf-open-bfd.c ctf-subr.c ctf-types.c ctf-util.c \
+	$(am__append_1)
 all: config.h
 	$(MAKE) $(AM_MAKEFLAGS) all-am
 
@@ -396,7 +401,6 @@  mostlyclean-compile:
 distclean-compile:
 	-rm -f *.tab.c
 
-@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/qsort_r.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-archive.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-create.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-decl.Po@am__quote@
@@ -407,6 +411,7 @@  distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-lookup.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-open-bfd.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-open.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-qsort_r.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-subr.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-types.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-util.Po@am__quote@
@@ -687,7 +692,7 @@  clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am
 
 distclean: distclean-am
 	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
-	-rm -rf $(DEPDIR) ./$(DEPDIR)
+	-rm -rf ./$(DEPDIR)
 	-rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
 	distclean-hdr distclean-tags
@@ -735,7 +740,7 @@  installcheck-am:
 maintainer-clean: maintainer-clean-am
 	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
 	-rm -rf $(top_srcdir)/autom4te.cache
-	-rm -rf $(DEPDIR) ./$(DEPDIR)
+	-rm -rf ./$(DEPDIR)
 	-rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
 
diff --git a/libctf/config.h.in b/libctf/config.h.in
index 0ecb5bb721..dff5501609 100644
--- a/libctf/config.h.in
+++ b/libctf/config.h.in
@@ -9,10 +9,6 @@ 
 /* Define to 1 if you have the <byteswap.h> header file. */
 #undef HAVE_BYTESWAP_H
 
-/* Define to 1 if you have the declaration of `qsort_r', and to 0 if you
-   don't. */
-#undef HAVE_DECL_QSORT_R
-
 /* Define to 1 if you have the <endian.h> header file. */
 #undef HAVE_ENDIAN_H
 
@@ -31,6 +27,9 @@ 
 /* Define to 1 if you have the `pread' function. */
 #undef HAVE_PREAD
 
+/* Whether a qsort_r exists with a void *arg as its last arg. */
+#undef HAVE_QSORT_R_ARG_LAST
+
 /* Define to 1 if you have the <stdint.h> header file. */
 #undef HAVE_STDINT_H
 
diff --git a/libctf/configure b/libctf/configure
index 4fb44eb2cc..c59e4c6abc 100755
--- a/libctf/configure
+++ b/libctf/configure
@@ -624,6 +624,8 @@  ac_subst_vars='am__EXEEXT_FALSE
 am__EXEEXT_TRUE
 LTLIBOBJS
 LIBOBJS
+NEED_CTF_QSORT_R_FALSE
+NEED_CTF_QSORT_R_TRUE
 zlibinc
 zlibdir
 ac_libctf_warn_cflags
@@ -1809,52 +1811,6 @@  $as_echo "$ac_res" >&6; }
   eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
 
 } # ac_fn_c_check_func
-
-# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES
-# ---------------------------------------------
-# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR
-# accordingly.
-ac_fn_c_check_decl ()
-{
-  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
-  as_decl_name=`echo $2|sed 's/ *(.*//'`
-  as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'`
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5
-$as_echo_n "checking whether $as_decl_name is declared... " >&6; }
-if eval \${$3+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-$4
-int
-main ()
-{
-#ifndef $as_decl_name
-#ifdef __cplusplus
-  (void) $as_decl_use;
-#else
-  (void) $as_decl_name;
-#endif
-#endif
-
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  eval "$3=yes"
-else
-  eval "$3=no"
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-eval ac_res=\$$3
-	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
-  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
-
-} # ac_fn_c_check_decl
 cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
@@ -6401,16 +6357,65 @@  _ACEOF
 fi
 done
 
-ac_fn_c_check_decl "$LINENO" "qsort_r" "ac_cv_have_decl_qsort_r" "$ac_includes_default"
-if test "x$ac_cv_have_decl_qsort_r" = xyes; then :
-  ac_have_decl=1
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for qsort_r with arg last" >&5
+$as_echo_n "checking for qsort_r with arg last... " >&6; }
+if ${ac_cv_libctf_qsort_r_arg_last+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test "$cross_compiling" = yes; then :
+  ac_cv_libctf_qsort_r_arg_last=no
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+  int do_not_call (const void *a, const void *b, void *arg)
+  {
+    return (a < b);
+  }
+  static int works = 0;
+  int conftest_compar (const void *a, const void *b, void *arg)
+  {
+    if (arg == do_not_call)
+      works = 1;
+    return (a < b);
+  }
+  int foo[2] = { 0, 1 };
+int
+main ()
+{
+qsort_r (foo, 2, sizeof (int), conftest_compar, do_not_call);
+   return (works == 0);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  ac_cv_libctf_qsort_r_arg_last=yes
 else
-  ac_have_decl=0
+  ac_cv_libctf_qsort_r_arg_last=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_libctf_qsort_r_arg_last" >&5
+$as_echo "$ac_cv_libctf_qsort_r_arg_last" >&6; }
+
+if test $ac_cv_libctf_qsort_r_arg_last = yes; then
+
+$as_echo "#define HAVE_QSORT_R_ARG_LAST 1" >>confdefs.h
+
+fi
+ if test "${ac_cv_libctf_qsort_r_arg_last}" != yes; then
+  NEED_CTF_QSORT_R_TRUE=
+  NEED_CTF_QSORT_R_FALSE='#'
+else
+  NEED_CTF_QSORT_R_TRUE='#'
+  NEED_CTF_QSORT_R_FALSE=
 fi
 
-cat >>confdefs.h <<_ACEOF
-#define HAVE_DECL_QSORT_R $ac_have_decl
-_ACEOF
 
 case " $LIBOBJS " in
   *" qsort_r.$ac_objext "* ) ;;
@@ -6561,6 +6566,10 @@  if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
 
+if test -z "${NEED_CTF_QSORT_R_TRUE}" && test -z "${NEED_CTF_QSORT_R_FALSE}"; then
+  as_fn_error $? "conditional \"NEED_CTF_QSORT_R\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 
 : "${CONFIG_STATUS=./config.status}"
 ac_write_fail=0
diff --git a/libctf/configure.ac b/libctf/configure.ac
index 8fd5388d2a..185aa94378 100644
--- a/libctf/configure.ac
+++ b/libctf/configure.ac
@@ -90,7 +90,35 @@  fi
 AC_C_BIGENDIAN
 AC_CHECK_HEADERS(byteswap.h endian.h)
 AC_CHECK_FUNCS(pread)
-AC_CHECK_DECLS([qsort_r])
+
+AC_CACHE_CHECK([for qsort_r with arg last], ac_cv_libctf_qsort_r_arg_last,
+[AC_RUN_IFELSE(
+  [AC_LANG_PROGRAM(
+  [#include <stdlib.h>
+  int do_not_call (const void *a, const void *b, void *arg)
+  {
+    return (a < b);
+  }
+  static int works = 0;
+  int conftest_compar (const void *a, const void *b, void *arg)
+  {
+    if (arg == do_not_call)
+      works = 1;
+    return (a < b);
+  }
+  int foo[[2]] = { 0, 1 };],
+  [qsort_r (foo, 2, sizeof (int), conftest_compar, do_not_call);
+   return (works == 0);])],
+  [ac_cv_libctf_qsort_r_arg_last=yes],
+  [ac_cv_libctf_qsort_r_arg_last=no],
+  [ac_cv_libctf_qsort_r_arg_last=no])])
+
+if test $ac_cv_libctf_qsort_r_arg_last = yes; then
+  AC_DEFINE([HAVE_QSORT_R_ARG_LAST], 1,
+	    [Whether a qsort_r exists with a void *arg as its last arg.])
+fi
+AM_CONDITIONAL(NEED_CTF_QSORT_R, test "${ac_cv_libctf_qsort_r_arg_last}" != yes)
+
 AC_LIBOBJ([qsort_r])
 
 AC_CONFIG_FILES(Makefile)
diff --git a/libctf/ctf-archive.c b/libctf/ctf-archive.c
index a238edb66b..90cd020b78 100644
--- a/libctf/ctf-archive.c
+++ b/libctf/ctf-archive.c
@@ -169,10 +169,11 @@  ctf_arc_write (const char *file, ctf_file_t ** ctf_files, size_t ctf_file_cnt,
       modent++;
     }
 
-  qsort_r ((ctf_archive_modent_t *) ((char *) archdr
-				     + sizeof (struct ctf_archive)),
-	   le64toh (archdr->ctfa_nfiles),
-	   sizeof (struct ctf_archive_modent), sort_modent_by_name, nametbl);
+  ctf_qsort_r ((ctf_archive_modent_t *) ((char *) archdr
+					 + sizeof (struct ctf_archive)),
+	       le64toh (archdr->ctfa_nfiles),
+	       sizeof (struct ctf_archive_modent), sort_modent_by_name,
+	       nametbl);
 
    /* Now the name table.  */
 
diff --git a/libctf/ctf-create.c b/libctf/ctf-create.c
index 227f62d8fd..3beed88712 100644
--- a/libctf/ctf-create.c
+++ b/libctf/ctf-create.c
@@ -344,7 +344,7 @@  ctf_update (ctf_file_t *fp)
     }
   assert (i == nvars);
 
-  qsort_r (dvarents, nvars, sizeof (ctf_varent_t), ctf_sort_var, s0);
+  ctf_qsort_r (dvarents, nvars, sizeof (ctf_varent_t), ctf_sort_var, s0);
   t += sizeof (ctf_varent_t) * nvars;
 
   assert (t == (unsigned char *) buf + sizeof (ctf_header_t) + hdr.cth_typeoff);
diff --git a/libctf/ctf-decls.h b/libctf/ctf-decls.h
index 5e9ede4809..1f8aab11e4 100644
--- a/libctf/ctf-decls.h
+++ b/libctf/ctf-decls.h
@@ -22,12 +22,22 @@ 
 
 #include "config.h"
 
-#if !HAVE_DECL_QSORT_R
 #include <stddef.h>
-void qsort_r (void *base, size_t nmemb, size_t size,
+#if HAVE_QSORT_R_ARG_LAST
+#include <stdlib.h>
+
+inline void
+ctf_qsort_r (void *base, size_t nmemb, size_t size,
+	     int (*compar)(const void *, const void *, void *),
+	     void *arg)
+{
+  qsort_r (base, nmemb, size, compar, arg);
+}
+#else
+void ctf_qsort_r (void *base, size_t nmemb, size_t size,
 	      int (*compar)(const void *, const void *, void *),
 	      void *arg);
-#endif /* !HAVE_DECL_QSORT_R */
+#endif /* !HAVE_QSORT_R_ARG_LAST */
 
 #undef MAX
 #undef MIN
diff --git a/libctf/qsort_r.c b/libctf/ctf-qsort_r.c
similarity index 99%
rename from libctf/qsort_r.c
rename to libctf/ctf-qsort_r.c
index 6c334fdaf3..6cb221d6f5 100644
--- a/libctf/qsort_r.c
+++ b/libctf/ctf-qsort_r.c
@@ -30,7 +30,7 @@ 
 #include "ctf-decls.h"
 
 #ifndef _LIBC
-# define _quicksort qsort_r
+# define _quicksort ctf_qsort_r
 # define __compar_d_fn_t compar_d_fn_t
 typedef int (*compar_d_fn_t) (const void *, const void *, void *);
 #endif