aboutsummaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
authorCarl Dong <contact@carldong.me>2021-06-25 20:49:33 -0400
committerCarl Dong <contact@carldong.me>2021-07-01 16:17:03 -0400
commit16b0a936e15b81710755303e11ef51f608b61475 (patch)
tree1f7bc959fc5453575da46a74aab8638e45bf6385 /contrib
parentddc6979b8baab048bbf0c5c2dd23345a8eb5c60e (diff)
downloadbitcoin-16b0a936e15b81710755303e11ef51f608b61475.tar.xz
guix: Rebase toolchain on glibc 2.24 (2.27 for riscv64)
Support for riscv64 in glibc landed in 2.27 so it's unavoidable that we use 2.27. Running a Bitcoin build with toolchains based on 2.24 for platforms other than riscv64 seem to produce binaries which do not have 2.17 symbols. So use 2.24 since it's more recent and maintained by Debian Stretch.
Diffstat (limited to 'contrib')
-rw-r--r--contrib/guix/manifest.scm43
-rw-r--r--contrib/guix/patches/glibc-2.24-elfm-loadaddr-dynamic-rewrite.patch60
-rw-r--r--contrib/guix/patches/glibc-2.24-no-build-time-cxx-header-run.patch98
-rw-r--r--contrib/guix/patches/glibc-2.27-riscv64-Use-__has_include__-to-include-asm-syscalls.h.patch70
-rw-r--r--contrib/guix/patches/glibc-ldd-x86_64.patch10
-rw-r--r--contrib/guix/patches/glibc-versioned-locpath.patch240
6 files changed, 519 insertions, 2 deletions
diff --git a/contrib/guix/manifest.scm b/contrib/guix/manifest.scm
index ba168a2a4a..f4df4855fc 100644
--- a/contrib/guix/manifest.scm
+++ b/contrib/guix/manifest.scm
@@ -135,11 +135,25 @@ chain for " target " development."))
(package-with-extra-patches gcc-8
(search-our-patches "gcc-8-sort-libtool-find-output.patch")))
+;; Building glibc with stack smashing protector first landed in glibc 2.25, use
+;; this function to disable for older glibcs
+;;
+;; From glibc 2.25 changelog:
+;;
+;; * Most of glibc can now be built with the stack smashing protector enabled.
+;; It is recommended to build glibc with --enable-stack-protector=strong.
+;; Implemented by Nick Alcock (Oracle).
+(define (make-glibc-without-ssp xglibc)
+ (package-with-extra-configure-variable
+ (package-with-extra-configure-variable
+ xglibc "libc_cv_ssp" "no")
+ "libc_cv_ssp_strong" "no"))
+
(define* (make-bitcoin-cross-toolchain target
#:key
(base-gcc-for-libc gcc-7)
(base-kernel-headers linux-libre-headers-5.4)
- (base-libc glibc) ; glibc 2.31
+ (base-libc (make-glibc-without-ssp glibc-2.24))
(base-gcc (make-gcc-rpath-link base-gcc)))
"Convenience wrapper around MAKE-CROSS-TOOLCHAIN with default values
desirable for building Bitcoin Core release binaries."
@@ -557,6 +571,28 @@ and endian independent.")
inspecting signatures in Mach-O binaries.")
(license license:expat))))
+(define-public glibc-2.24
+ (package
+ (inherit glibc)
+ (version "2.24")
+ (source (origin
+ (method git-fetch)
+ (uri (git-reference
+ (url "https://sourceware.org/git/glibc.git")
+ (commit "0d7f1ed30969886c8dde62fbf7d2c79967d4bace")))
+ (file-name (git-file-name "glibc" "0d7f1ed30969886c8dde62fbf7d2c79967d4bace"))
+ (sha256
+ (base32
+ "0g5hryia5v1k0qx97qffgwzrz4lr4jw3s5kj04yllhswsxyjbic3"))
+ (patches (search-our-patches "glibc-ldd-x86_64.patch"
+ "glibc-versioned-locpath.patch"
+ "glibc-2.24-elfm-loadaddr-dynamic-rewrite.patch"
+ "glibc-2.24-no-build-time-cxx-header-run.patch"))))))
+
+(define glibc-2.27/bitcoin-patched
+ (package-with-extra-patches glibc-2.27
+ (search-our-patches "glibc-2.27-riscv64-Use-__has_include__-to-include-asm-syscalls.h.patch")))
+
(packages->manifest
(append
(list ;; The Basics
@@ -606,7 +642,10 @@ inspecting signatures in Mach-O binaries.")
(make-nsis-with-sde-support nsis-x86_64)
osslsigncode))
((string-contains target "-linux-")
- (list (make-bitcoin-cross-toolchain target)))
+ (list (cond ((string-contains target "riscv64-")
+ (make-bitcoin-cross-toolchain target #:base-libc glibc-2.27/bitcoin-patched))
+ (else
+ (make-bitcoin-cross-toolchain target)))))
((string-contains target "darwin")
(list clang-toolchain-10 binutils imagemagick libtiff librsvg font-tuffy cmake xorriso python-signapple))
(else '())))))
diff --git a/contrib/guix/patches/glibc-2.24-elfm-loadaddr-dynamic-rewrite.patch b/contrib/guix/patches/glibc-2.24-elfm-loadaddr-dynamic-rewrite.patch
new file mode 100644
index 0000000000..54a7824345
--- /dev/null
+++ b/contrib/guix/patches/glibc-2.24-elfm-loadaddr-dynamic-rewrite.patch
@@ -0,0 +1,60 @@
+commit 6b02af31e9a721bb15a11380cd22d53b621711f8
+Author: Szabolcs Nagy <szabolcs.nagy@arm.com>
+Date: Wed Oct 18 17:26:23 2017 +0100
+
+ [AARCH64] Rewrite elf_machine_load_address using _DYNAMIC symbol
+
+ This patch rewrites aarch64 elf_machine_load_address to use special _DYNAMIC
+ symbol instead of _dl_start.
+
+ The static address of _DYNAMIC symbol is stored in the first GOT entry.
+ Here is the change which makes this solution work (part of binutils 2.24):
+ https://sourceware.org/ml/binutils/2013-06/msg00248.html
+
+ i386, x86_64 targets use the same method to do this as well.
+
+ The original implementation relies on a trick that R_AARCH64_ABS32 relocation
+ being resolved at link time and the static address fits in the 32bits.
+ However, in LP64, normally, the address is defined to be 64 bit.
+
+ Here is the C version one which should be portable in all cases.
+
+ * sysdeps/aarch64/dl-machine.h (elf_machine_load_address): Use
+ _DYNAMIC symbol to calculate load address.
+
+diff --git a/sysdeps/aarch64/dl-machine.h b/sysdeps/aarch64/dl-machine.h
+index e86d8b5b63..5a5b8a5de5 100644
+--- a/sysdeps/aarch64/dl-machine.h
++++ b/sysdeps/aarch64/dl-machine.h
+@@ -49,26 +49,11 @@ elf_machine_load_address (void)
+ /* To figure out the load address we use the definition that for any symbol:
+ dynamic_addr(symbol) = static_addr(symbol) + load_addr
+
+- The choice of symbol is arbitrary. The static address we obtain
+- by constructing a non GOT reference to the symbol, the dynamic
+- address of the symbol we compute using adrp/add to compute the
+- symbol's address relative to the PC.
+- This depends on 32bit relocations being resolved at link time
+- and that the static address fits in the 32bits. */
+-
+- ElfW(Addr) static_addr;
+- ElfW(Addr) dynamic_addr;
+-
+- asm (" \n"
+-" adrp %1, _dl_start; \n"
+-" add %1, %1, #:lo12:_dl_start \n"
+-" ldr %w0, 1f \n"
+-" b 2f \n"
+-"1: \n"
+-" .word _dl_start \n"
+-"2: \n"
+- : "=r" (static_addr), "=r" (dynamic_addr));
+- return dynamic_addr - static_addr;
++ _DYNAMIC sysmbol is used here as its link-time address stored in
++ the special unrelocated first GOT entry. */
++
++ extern ElfW(Dyn) _DYNAMIC[] attribute_hidden;
++ return (ElfW(Addr)) &_DYNAMIC - elf_machine_dynamic ();
+ }
+
+ /* Set up the loaded object described by L so its unrelocated PLT
diff --git a/contrib/guix/patches/glibc-2.24-no-build-time-cxx-header-run.patch b/contrib/guix/patches/glibc-2.24-no-build-time-cxx-header-run.patch
new file mode 100644
index 0000000000..5d7a148c62
--- /dev/null
+++ b/contrib/guix/patches/glibc-2.24-no-build-time-cxx-header-run.patch
@@ -0,0 +1,98 @@
+commit dc23a45db566095e83ff0b7a57afc87fb5ca89a1
+Author: Florian Weimer <fweimer@redhat.com>
+Date: Wed Sep 21 10:45:32 2016 +0200
+
+ Avoid running $(CXX) during build to obtain header file paths
+
+ This reduces the build time somewhat and is particularly noticeable
+ during rebuilds with few code changes.
+
+diff --git a/Makerules b/Makerules
+index 7e4077ee50..c338850de5 100644
+--- a/Makerules
++++ b/Makerules
+@@ -121,14 +121,10 @@ ifneq (,$(CXX))
+ # will be used instead of /usr/include/stdlib.h and /usr/include/math.h.
+ before-compile := $(common-objpfx)cstdlib $(common-objpfx)cmath \
+ $(before-compile)
+-cstdlib=$(shell echo "\#include <cstdlib>" | $(CXX) -M -MP -x c++ - \
+- | sed -n "/cstdlib:/{s/:$$//;p}")
+-$(common-objpfx)cstdlib: $(cstdlib)
++$(common-objpfx)cstdlib: $(c++-cstdlib-header)
+ $(INSTALL_DATA) $< $@T
+ $(move-if-change) $@T $@
+-cmath=$(shell echo "\#include <cmath>" | $(CXX) -M -MP -x c++ - \
+- | sed -n "/cmath:/{s/:$$//;p}")
+-$(common-objpfx)cmath: $(cmath)
++$(common-objpfx)cmath: $(c++-cmath-header)
+ $(INSTALL_DATA) $< $@T
+ $(move-if-change) $@T $@
+ endif
+diff --git a/config.make.in b/config.make.in
+index 95c6f36876..04a8b3ed7f 100644
+--- a/config.make.in
++++ b/config.make.in
+@@ -45,6 +45,8 @@ defines = @DEFINES@
+ sysheaders = @sysheaders@
+ sysincludes = @SYSINCLUDES@
+ c++-sysincludes = @CXX_SYSINCLUDES@
++c++-cstdlib-header = @CXX_CSTDLIB_HEADER@
++c++-cmath-header = @CXX_CMATH_HEADER@
+ all-warnings = @all_warnings@
+ enable-werror = @enable_werror@
+
+diff --git a/configure b/configure
+index 17625e1041..6ff252744b 100755
+--- a/configure
++++ b/configure
+@@ -635,6 +635,8 @@ BISON
+ INSTALL_INFO
+ PERL
+ BASH_SHELL
++CXX_CMATH_HEADER
++CXX_CSTDLIB_HEADER
+ CXX_SYSINCLUDES
+ SYSINCLUDES
+ AUTOCONF
+@@ -5054,6 +5056,18 @@ fi
+
+
+
++# Obtain some C++ header file paths. This is used to make a local
++# copy of those headers in Makerules.
++if test -n "$CXX"; then
++ find_cxx_header () {
++ echo "#include <$1>" | $CXX -M -MP -x c++ - | sed -n "/$1:/{s/:\$//;p}"
++ }
++ CXX_CSTDLIB_HEADER="$(find_cxx_header cstdlib)"
++ CXX_CMATH_HEADER="$(find_cxx_header cmath)"
++fi
++
++
++
+ # Test if LD_LIBRARY_PATH contains the notation for the current directory
+ # since this would lead to problems installing/building glibc.
+ # LD_LIBRARY_PATH contains the current directory if one of the following
+diff --git a/configure.ac b/configure.ac
+index 33bcd62180..9938ab0dc2 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1039,6 +1039,18 @@ fi
+ AC_SUBST(SYSINCLUDES)
+ AC_SUBST(CXX_SYSINCLUDES)
+
++# Obtain some C++ header file paths. This is used to make a local
++# copy of those headers in Makerules.
++if test -n "$CXX"; then
++ find_cxx_header () {
++ echo "#include <$1>" | $CXX -M -MP -x c++ - | sed -n "/$1:/{s/:\$//;p}"
++ }
++ CXX_CSTDLIB_HEADER="$(find_cxx_header cstdlib)"
++ CXX_CMATH_HEADER="$(find_cxx_header cmath)"
++fi
++AC_SUBST(CXX_CSTDLIB_HEADER)
++AC_SUBST(CXX_CMATH_HEADER)
++
+ # Test if LD_LIBRARY_PATH contains the notation for the current directory
+ # since this would lead to problems installing/building glibc.
+ # LD_LIBRARY_PATH contains the current directory if one of the following
diff --git a/contrib/guix/patches/glibc-2.27-riscv64-Use-__has_include__-to-include-asm-syscalls.h.patch b/contrib/guix/patches/glibc-2.27-riscv64-Use-__has_include__-to-include-asm-syscalls.h.patch
new file mode 100644
index 0000000000..39c995ffb5
--- /dev/null
+++ b/contrib/guix/patches/glibc-2.27-riscv64-Use-__has_include__-to-include-asm-syscalls.h.patch
@@ -0,0 +1,70 @@
+From 562c52cc81a4e456a62e6455feb32732049e9070 Mon Sep 17 00:00:00 2001
+From: "H.J. Lu" <hjl.tools@gmail.com>
+Date: Mon, 31 Dec 2018 09:26:42 -0800
+Subject: [PATCH] riscv: Use __has_include__ to include <asm/syscalls.h> [BZ
+ #24022]
+
+<asm/syscalls.h> has been removed by
+
+commit 27f8899d6002e11a6e2d995e29b8deab5aa9cc25
+Author: David Abdurachmanov <david.abdurachmanov@gmail.com>
+Date: Thu Nov 8 20:02:39 2018 +0100
+
+ riscv: add asm/unistd.h UAPI header
+
+ Marcin Juszkiewicz reported issues while generating syscall table for riscv
+ using 4.20-rc1. The patch refactors our unistd.h files to match some other
+ architectures.
+
+ - Add asm/unistd.h UAPI header, which has __ARCH_WANT_NEW_STAT only for 64-bit
+ - Remove asm/syscalls.h UAPI header and merge to asm/unistd.h
+ - Adjust kernel asm/unistd.h
+
+ So now asm/unistd.h UAPI header should show all syscalls for riscv.
+
+<asm/syscalls.h> may be restored by
+
+Subject: [PATCH] riscv: restore asm/syscalls.h UAPI header
+Date: Tue, 11 Dec 2018 09:09:35 +0100
+
+UAPI header asm/syscalls.h was merged into UAPI asm/unistd.h header,
+which did resolve issue with missing syscalls macros resulting in
+glibc (2.28) build failure. It also broke glibc in a different way:
+asm/syscalls.h is being used by glibc. I noticed this while doing
+Fedora 30/Rawhide mass rebuild.
+
+The patch returns asm/syscalls.h header and incl. it into asm/unistd.h.
+I plan to send a patch to glibc to use asm/unistd.h instead of
+asm/syscalls.h
+
+In the meantime, we use __has_include__, which was added to GCC 5, to
+check if <asm/syscalls.h> exists before including it. Tested with
+build-many-glibcs.py for riscv against kernel 4.19.12 and 4.20-rc7.
+
+ [BZ #24022]
+ * sysdeps/unix/sysv/linux/riscv/flush-icache.c: Check if
+ <asm/syscalls.h> exists with __has_include__ before including it.
+---
+ sysdeps/unix/sysv/linux/riscv/flush-icache.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/sysdeps/unix/sysv/linux/riscv/flush-icache.c b/sysdeps/unix/sysv/linux/riscv/flush-icache.c
+index d612ef4c6c..0b2042620b 100644
+--- a/sysdeps/unix/sysv/linux/riscv/flush-icache.c
++++ b/sysdeps/unix/sysv/linux/riscv/flush-icache.c
+@@ -21,7 +21,11 @@
+ #include <stdlib.h>
+ #include <atomic.h>
+ #include <sys/cachectl.h>
+-#include <asm/syscalls.h>
++#if __has_include__ (<asm/syscalls.h>)
++# include <asm/syscalls.h>
++#else
++# include <asm/unistd.h>
++#endif
+
+ typedef int (*func_type) (void *, void *, unsigned long int);
+
+--
+2.31.1
+
diff --git a/contrib/guix/patches/glibc-ldd-x86_64.patch b/contrib/guix/patches/glibc-ldd-x86_64.patch
new file mode 100644
index 0000000000..b1b6d5a548
--- /dev/null
+++ b/contrib/guix/patches/glibc-ldd-x86_64.patch
@@ -0,0 +1,10 @@
+By default, 'RTDLLIST' in 'ldd' refers to 'lib64/ld-linux-x86-64.so', whereas
+it's in 'lib/' for us. This patch fixes that.
+
+--- glibc-2.17/sysdeps/unix/sysv/linux/x86_64/ldd-rewrite.sed 2012-12-25 04:02:13.000000000 +0100
++++ glibc-2.17/sysdeps/unix/sysv/linux/x86_64/ldd-rewrite.sed 2013-09-15 23:08:03.000000000 +0200
+@@ -1,3 +1,3 @@
+ /LD_TRACE_LOADED_OBJECTS=1/a\
+ add_env="$add_env LD_LIBRARY_VERSION=\\$verify_out"
+-s_^\(RTLDLIST=\)\(.*lib\)\(\|64\|x32\)\(/[^/]*\)\(-x86-64\|-x32\)\(\.so\.[0-9.]*\)[ ]*$_\1"\2\4\6 \264\4-x86-64\6 \2x32\4-x32\6"_
++s_^\(RTLDLIST=\)\(.*lib\)\(\|64\|x32\)\(/[^/]*\)\(-x86-64\|-x32\)\(\.so\.[0-9.]*\)[ ]*$_\1"\2\4\6 \2\4-x86-64\6 \2x32\4-x32\6"_
diff --git a/contrib/guix/patches/glibc-versioned-locpath.patch b/contrib/guix/patches/glibc-versioned-locpath.patch
new file mode 100644
index 0000000000..bc7652127f
--- /dev/null
+++ b/contrib/guix/patches/glibc-versioned-locpath.patch
@@ -0,0 +1,240 @@
+The format of locale data can be incompatible between libc versions, and
+loading incompatible data can lead to 'setlocale' returning EINVAL at best
+or triggering an assertion failure at worst. See
+https://lists.gnu.org/archive/html/guix-devel/2015-09/msg00717.html
+for background information.
+
+To address that, this patch changes libc to honor a new 'GUIX_LOCPATH'
+variable, and to look for locale data in version-specific sub-directories of
+that variable. So, if GUIX_LOCPATH=/foo:/bar, locale data is searched for in
+/foo/X.Y and /bar/X.Y, where X.Y is the libc version number.
+
+That way, a single 'GUIX_LOCPATH' setting can work even if different libc
+versions coexist on the system.
+
+--- a/locale/newlocale.c
++++ b/locale/newlocale.c
+@@ -30,6 +30,7 @@
+ /* Lock for protecting global data. */
+ __libc_rwlock_define (extern , __libc_setlocale_lock attribute_hidden)
+
++extern error_t compute_locale_search_path (char **, size_t *);
+
+ /* Use this when we come along an error. */
+ #define ERROR_RETURN \
+@@ -48,7 +49,6 @@ __newlocale (int category_mask, const char *locale, __locale_t base)
+ __locale_t result_ptr;
+ char *locale_path;
+ size_t locale_path_len;
+- const char *locpath_var;
+ int cnt;
+ size_t names_len;
+
+@@ -102,17 +102,8 @@ __newlocale (int category_mask, const char *locale, __locale_t base)
+ locale_path = NULL;
+ locale_path_len = 0;
+
+- locpath_var = getenv ("LOCPATH");
+- if (locpath_var != NULL && locpath_var[0] != '\0')
+- {
+- if (__argz_create_sep (locpath_var, ':',
+- &locale_path, &locale_path_len) != 0)
+- return NULL;
+-
+- if (__argz_add_sep (&locale_path, &locale_path_len,
+- _nl_default_locale_path, ':') != 0)
+- return NULL;
+- }
++ if (compute_locale_search_path (&locale_path, &locale_path_len) != 0)
++ return NULL;
+
+ /* Get the names for the locales we are interested in. We either
+ allow a composite name or a single name. */
+diff --git a/locale/setlocale.c b/locale/setlocale.c
+index ead030d..0c0e314 100644
+--- a/locale/setlocale.c
++++ b/locale/setlocale.c
+@@ -215,12 +215,65 @@ setdata (int category, struct __locale_data *data)
+ }
+ }
+
++/* Return in *LOCALE_PATH and *LOCALE_PATH_LEN the locale data search path as
++ a colon-separated list. Return ENOMEN on error, zero otherwise. */
++error_t
++compute_locale_search_path (char **locale_path, size_t *locale_path_len)
++{
++ char* guix_locpath_var = getenv ("GUIX_LOCPATH");
++ char *locpath_var = getenv ("LOCPATH");
++
++ if (guix_locpath_var != NULL && guix_locpath_var[0] != '\0')
++ {
++ /* Entries in 'GUIX_LOCPATH' take precedence over 'LOCPATH'. These
++ entries are systematically prefixed with "/X.Y" where "X.Y" is the
++ libc version. */
++ if (__argz_create_sep (guix_locpath_var, ':',
++ locale_path, locale_path_len) != 0
++ || __argz_suffix_entries (locale_path, locale_path_len,
++ "/" VERSION) != 0)
++ goto bail_out;
++ }
++
++ if (locpath_var != NULL && locpath_var[0] != '\0')
++ {
++ char *reg_locale_path = NULL;
++ size_t reg_locale_path_len = 0;
++
++ if (__argz_create_sep (locpath_var, ':',
++ &reg_locale_path, &reg_locale_path_len) != 0)
++ goto bail_out;
++
++ if (__argz_append (locale_path, locale_path_len,
++ reg_locale_path, reg_locale_path_len) != 0)
++ goto bail_out;
++
++ free (reg_locale_path);
++ }
++
++ if (*locale_path != NULL)
++ {
++ /* Append the system default locale directory. */
++ if (__argz_add_sep (locale_path, locale_path_len,
++ _nl_default_locale_path, ':') != 0)
++ goto bail_out;
++ }
++
++ return 0;
++
++ bail_out:
++ free (*locale_path);
++ *locale_path = NULL;
++ *locale_path_len = 0;
++
++ return ENOMEM;
++}
++
+ char *
+ setlocale (int category, const char *locale)
+ {
+ char *locale_path;
+ size_t locale_path_len;
+- const char *locpath_var;
+ char *composite;
+
+ /* Sanity check for CATEGORY argument. */
+@@ -251,17 +304,10 @@ setlocale (int category, const char *locale)
+ locale_path = NULL;
+ locale_path_len = 0;
+
+- locpath_var = getenv ("LOCPATH");
+- if (locpath_var != NULL && locpath_var[0] != '\0')
++ if (compute_locale_search_path (&locale_path, &locale_path_len) != 0)
+ {
+- if (__argz_create_sep (locpath_var, ':',
+- &locale_path, &locale_path_len) != 0
+- || __argz_add_sep (&locale_path, &locale_path_len,
+- _nl_default_locale_path, ':') != 0)
+- {
+- __libc_rwlock_unlock (__libc_setlocale_lock);
+- return NULL;
+- }
++ __libc_rwlock_unlock (__libc_setlocale_lock);
++ return NULL;
+ }
+
+ if (category == LC_ALL)
+diff --git a/string/Makefile b/string/Makefile
+index 8424a61..f925503 100644
+--- a/string/Makefile
++++ b/string/Makefile
+@@ -38,7 +38,7 @@ routines := strcat strchr strcmp strcoll strcpy strcspn \
+ swab strfry memfrob memmem rawmemchr strchrnul \
+ $(addprefix argz-,append count create ctsep next \
+ delete extract insert stringify \
+- addsep replace) \
++ addsep replace suffix) \
+ envz basename \
+ strcoll_l strxfrm_l string-inlines memrchr \
+ xpg-strerror strerror_l
+diff --git a/string/argz-suffix.c b/string/argz-suffix.c
+new file mode 100644
+index 0000000..505b0f2
+--- /dev/null
++++ b/string/argz-suffix.c
+@@ -0,0 +1,56 @@
++/* Copyright (C) 2015 Free Software Foundation, Inc.
++ This file is part of the GNU C Library.
++ Contributed by Ludovic Courtès <ludo@gnu.org>.
++
++ The GNU C Library is free software; you can redistribute it and/or
++ modify it under the terms of the GNU Lesser General Public
++ License as published by the Free Software Foundation; either
++ version 2.1 of the License, or (at your option) any later version.
++
++ The GNU C Library 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
++ Lesser General Public License for more details.
++
++ You should have received a copy of the GNU Lesser General Public
++ License along with the GNU C Library; if not, see
++ <http://www.gnu.org/licenses/>. */
++
++#include <argz.h>
++#include <errno.h>
++#include <stdlib.h>
++#include <string.h>
++
++
++error_t
++__argz_suffix_entries (char **argz, size_t *argz_len, const char *suffix)
++
++{
++ size_t suffix_len = strlen (suffix);
++ size_t count = __argz_count (*argz, *argz_len);
++ size_t new_argz_len = *argz_len + count * suffix_len;
++ char *new_argz = malloc (new_argz_len);
++
++ if (new_argz)
++ {
++ char *p = new_argz, *entry;
++
++ for (entry = *argz;
++ entry != NULL;
++ entry = argz_next (*argz, *argz_len, entry))
++ {
++ p = stpcpy (p, entry);
++ p = stpcpy (p, suffix);
++ p++;
++ }
++
++ free (*argz);
++ *argz = new_argz;
++ *argz_len = new_argz_len;
++
++ return 0;
++ }
++ else
++ return ENOMEM;
++}
++weak_alias (__argz_suffix_entries, argz_suffix_entries)
+diff --git a/string/argz.h b/string/argz.h
+index bb62a31..d276a35 100644
+--- a/string/argz.h
++++ b/string/argz.h
+@@ -134,6 +134,16 @@ extern error_t argz_replace (char **__restrict __argz,
+ const char *__restrict __str,
+ const char *__restrict __with,
+ unsigned int *__restrict __replace_count);
++
++/* Suffix each entry of ARGZ & ARGZ_LEN with SUFFIX. Return 0 on success,
++ and ENOMEN if memory cannot be allocated. */
++extern error_t __argz_suffix_entries (char **__restrict __argz,
++ size_t *__restrict __argz_len,
++ const char *__restrict __suffix);
++extern error_t argz_suffix_entries (char **__restrict __argz,
++ size_t *__restrict __argz_len,
++ const char *__restrict __suffix);
++
+
+ /* Returns the next entry in ARGZ & ARGZ_LEN after ENTRY, or NULL if there
+ are no more. If entry is NULL, then the first entry is returned. This