From 737f7e8513f66db4247e2f5f301b2503633a809e Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Fri, 19 Jan 2018 16:32:18 +0100 Subject: [PATCH] glibc_post_upgrade: Integrate into the build process This gives us access to the relevant definitions and also enables us to perform a static PIE build without replicate the entire compiler invocation. Due to the move into the glibc build process, the program had to be cleaned up to compile without warnings. --- glibc-post_upgrade.patch | 360 +++++++++++++++++++++++++++++++++++++++ glibc.spec | 23 +-- 2 files changed, 366 insertions(+), 17 deletions(-) create mode 100644 glibc-post_upgrade.patch diff --git a/glibc-post_upgrade.patch b/glibc-post_upgrade.patch new file mode 100644 index 0000000..075660c --- /dev/null +++ b/glibc-post_upgrade.patch @@ -0,0 +1,360 @@ +A helper program is needed to clean up the system configuration +early during RPM package installation, so that other scriptlets +can run successfully. + +diff --git a/elf/Makefile b/elf/Makefile +index 2a432d8beebcd207..368dcae477fff2ae 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -117,6 +117,14 @@ others-extras = $(ldconfig-modules) + endif + endif + ++# This needs to be statically linked because it is executed at a time ++# when there might be incompatible shared objects on disk, and the ++# purpose of this program is to remove them (among other things). ++others-static += glibc_post_upgrade ++others += glibc_post_upgrade ++glibc_post_upgrade-modules := static-stubs ++CFLAGS-glibc_post_upgrade.c += -DGCONV_MODULES_DIR='"$(gconvdir)"' ++ + # To find xmalloc.c and xstrdup.c + vpath %.c ../locale/programs + +@@ -559,6 +567,8 @@ $(objpfx)sln: $(sln-modules:%=$(objpfx)%.o) + + $(objpfx)ldconfig: $(ldconfig-modules:%=$(objpfx)%.o) + ++$(objpfx)glibc_post_upgrade: $(glibc_post_upgrade-modules:%=$(objpfx)%.o) ++ + SYSCONF-FLAGS := -D'SYSCONFDIR="$(sysconfdir)"' + CFLAGS-ldconfig.c += $(SYSCONF-FLAGS) -D'LIBDIR="$(libdir)"' \ + -D'SLIBDIR="$(slibdir)"' +diff --git a/elf/glibc_post_upgrade.c b/elf/glibc_post_upgrade.c +new file mode 100644 +index 0000000000000000..3c9839ae523d2cc7 +--- /dev/null ++++ b/elf/glibc_post_upgrade.c +@@ -0,0 +1,322 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define LD_SO_CONF "/etc/ld.so.conf" ++#define ICONVCONFIG "/usr/sbin/iconvconfig" ++ ++#define verbose_exec(failcode, path...) \ ++ do \ ++ { \ ++ char *const arr[] = { path, NULL }; \ ++ vexec (failcode, arr); \ ++ } while (0) ++ ++__attribute__((noinline)) static void vexec (int failcode, char *const path[]); ++__attribute__((noinline)) static void says (const char *str); ++__attribute__((noinline)) static void sayn (long num); ++__attribute__((noinline)) static void message (char *const path[]); ++__attribute__((noinline)) static int check_elf (const char *name); ++ ++int ++main (void) ++{ ++ struct stat statbuf; ++ char initpath[256]; ++ ++ char buffer[4096]; ++ struct pref { ++ const char *p; ++ int len; ++ } prefix[] = { { "libc-", 5 }, { "libm-", 5 }, ++ { "librt-", 6 }, { "libpthread-", 11 }, ++ { "librtkaio-", 10 }, { "libthread_db-", 13 } }; ++ int i, j, fd; ++ off_t base; ++ ssize_t ret; ++ ++ /* In order to support in-place upgrades, we must immediately remove ++ obsolete platform directories after installing a new glibc ++ version. RPM only deletes files removed by updates near the end ++ of the transaction. If we did not remove the obsolete platform ++ directories here, they would be preferred by the dynamic linker ++ during the execution of subsequent RPM scriptlets, likely ++ resulting in process startup failures. */ ++ const char *remove_dirs[] = ++ { ++#if defined (__i386__) ++ "/lib/i686", ++ "/lib/i686/nosegneg", ++#elif defined (__powerpc64__) && _CALL_ELF != 2 ++ "/lib64/power6", ++#endif ++ }; ++ for (j = 0; j < sizeof (remove_dirs) / sizeof (remove_dirs[0]); ++j) ++ { ++ size_t rmlen = strlen (remove_dirs[j]); ++ fd = open (remove_dirs[j], O_RDONLY); ++ if (fd >= 0 ++ && (ret = getdirentries (fd, buffer, sizeof (buffer), &base)) ++ >= (ssize_t) offsetof (struct dirent, d_name)) ++ { ++ for (base = 0; base + offsetof (struct dirent, d_name) < ret; ) ++ { ++ struct dirent *d = (struct dirent *) (buffer + base); ++ ++ for (i = 0; i < sizeof (prefix) / sizeof (prefix[0]); i++) ++ if (! strncmp (d->d_name, prefix[i].p, prefix[i].len)) ++ { ++ char *p = d->d_name + prefix[i].len; ++ ++ while (*p == '.' || (*p >= '0' && *p <= '9')) p++; ++ if (p[0] == 's' && p[1] == 'o' && p[2] == '\0' ++ && p + 3 - d->d_name ++ < sizeof (initpath) - rmlen - 1) ++ { ++ memcpy (initpath, remove_dirs[j], rmlen); ++ initpath[rmlen] = '/'; ++ strcpy (initpath + rmlen + 1, d->d_name); ++ unlink (initpath); ++ break; ++ } ++ } ++ base += d->d_reclen; ++ } ++ close (fd); ++ } ++ } ++ ++ int ldsocfd = open (LD_SO_CONF, O_RDONLY); ++ struct stat ldsocst; ++ if (ldsocfd >= 0 && fstat (ldsocfd, &ldsocst) >= 0) ++ { ++ char p[ldsocst.st_size + 1]; ++ if (read (ldsocfd, p, ldsocst.st_size) == ldsocst.st_size) ++ { ++ p[ldsocst.st_size] = '\0'; ++ if (strstr (p, "include ld.so.conf.d/*.conf") == NULL) ++ { ++ close (ldsocfd); ++ ldsocfd = open (LD_SO_CONF, O_WRONLY | O_TRUNC); ++ if (ldsocfd >= 0) ++ { ++ size_t slen = strlen ("include ld.so.conf.d/*.conf\n"); ++ if (write (ldsocfd, "include ld.so.conf.d/*.conf\n", slen) ++ != slen ++ || write (ldsocfd, p, ldsocst.st_size) != ldsocst.st_size) ++ _exit (109); ++ } ++ } ++ } ++ if (ldsocfd >= 0) ++ close (ldsocfd); ++ } ++ ++ /* If installing bi-arch glibc, rpm sometimes doesn't unpack all files ++ before running one of the lib's %post scriptlet. /sbin/ldconfig will ++ then be run by the other arch's %post. */ ++ if (! access ("/sbin/ldconfig", X_OK)) ++ verbose_exec (110, ++ (char *) "/sbin/ldconfig", ++ (char *) "/sbin/ldconfig"); ++ ++ if (! utimes (GCONV_MODULES_DIR "/gconv-modules.cache", NULL)) ++ { ++ const char *iconv_cache = GCONV_MODULES_DIR "/gconv-modules.cache"; ++ const char *iconv_dir = GCONV_MODULES_DIR; ++ verbose_exec (113, ++ (char *) ICONVCONFIG, ++ (char *) "/usr/sbin/iconvconfig", ++ (char *) "-o", ++ (char *) iconv_cache, ++ (char *) "--nostdlib", ++ (char *) iconv_dir); ++ } ++ ++ /* Check if telinit is available and either SysVInit fifo, ++ or upstart telinit. */ ++ if (access ("/sbin/telinit", X_OK) ++ || ((!!access ("/dev/initctl", F_OK)) ++ ^ !access ("/sbin/initctl", X_OK))) ++ _exit (0); ++ ++ /* Check if we are not inside of some chroot, because we'd just ++ timeout and leave /etc/initrunlvl. ++ ++ On more modern systems this test is not sufficient to detect ++ if we're in a chroot. */ ++ if (readlink ("/proc/1/exe", initpath, 256) <= 0 || ++ readlink ("/proc/1/root", initpath, 256) <= 0) ++ _exit (0); ++ ++ /* Here's another well known way to detect chroot, at least on an ++ ext and xfs filesystems and assuming nothing mounted on the chroot's ++ root. */ ++ if (stat ("/", &statbuf) != 0 ++ || (statbuf.st_ino != 2 ++ && statbuf.st_ino != 128)) ++ _exit (0); ++ ++ if (check_elf ("/proc/1/exe")) ++ verbose_exec (116, ++ (char *) "/sbin/telinit", ++ (char *) "/sbin/telinit", ++ (char *) "u"); ++ ++ /* Check if we can safely condrestart sshd. */ ++ if (access ("/sbin/service", X_OK) == 0 ++ && access ("/usr/sbin/sshd", X_OK) == 0 ++ && access ("/etc/rc.d/init.d/sshd", X_OK) == 0 ++ && access ("/bin/bash", X_OK) == 0) ++ { ++ if (check_elf ("/usr/sbin/sshd")) ++ verbose_exec (-121, ++ (char *) "/sbin/service", ++ (char *) "/sbin/service", ++ (char *) "sshd", ++ (char *) "condrestart"); ++ } ++ ++ _exit(0); ++} ++ ++void ++vexec (int failcode, char *const path[]) ++{ ++ pid_t pid; ++ int status, save_errno; ++ int devnull = 0; ++ ++ if (failcode < 0) ++ { ++ devnull = 1; ++ failcode = -failcode; ++ } ++ pid = vfork (); ++ if (pid == 0) ++ { ++ int fd; ++ if (devnull && (fd = open ("/dev/null", O_WRONLY)) >= 0) ++ { ++ dup2 (fd, 1); ++ dup2 (fd, 2); ++ close (fd); ++ } ++ execv (path[0], path + 1); ++ save_errno = errno; ++ message (path); ++ says (" exec failed with errno "); ++ sayn (save_errno); ++ says ("\n"); ++ _exit (failcode); ++ } ++ else if (pid < 0) ++ { ++ save_errno = errno; ++ message (path); ++ says (" fork failed with errno "); ++ sayn (save_errno); ++ says ("\n"); ++ _exit (failcode + 1); ++ } ++ if (waitpid (0, &status, 0) != pid || !WIFEXITED (status)) ++ { ++ message (path); ++ says (" child terminated abnormally\n"); ++ _exit (failcode + 2); ++ } ++ if (WEXITSTATUS (status)) ++ { ++ message (path); ++ says (" child exited with exit code "); ++ sayn (WEXITSTATUS (status)); ++ says ("\n"); ++ _exit (WEXITSTATUS (status)); ++ } ++} ++ ++static void ++says (const char *str) ++{ ++ write (1, str, strlen (str)); ++} ++ ++static void ++sayn (long num) ++{ ++ char string[sizeof (long) * 3 + 1]; ++ char *p = string + sizeof (string) - 1; ++ ++ *p = '\0'; ++ if (num == 0) ++ *--p = '0'; ++ else ++ while (num) ++ { ++ *--p = '0' + num % 10; ++ num = num / 10; ++ } ++ ++ says (p); ++} ++ ++static void ++message (char *const path[]) ++{ ++ says ("/usr/sbin/glibc_post_upgrade: While trying to execute "); ++ says (path[0]); ++} ++ ++static int ++check_elf (const char *name) ++{ ++ /* Play safe, if we can't open or read, assume it might be ++ ELF for the current arch. */ ++ int ret = 1; ++ int fd = open (name, O_RDONLY); ++ if (fd >= 0) ++ { ++ Elf32_Ehdr ehdr; ++ if (read (fd, &ehdr, offsetof (Elf32_Ehdr, e_version)) ++ == offsetof (Elf32_Ehdr, e_version)) ++ { ++ ret = 0; ++ if (ehdr.e_ident[EI_CLASS] ++ == (sizeof (long) == 8 ? ELFCLASS64 : ELFCLASS32)) ++ { ++#if defined __i386__ ++ ret = ehdr.e_machine == EM_386; ++#elif defined __x86_64__ ++ ret = ehdr.e_machine == EM_X86_64; ++#elif defined __powerpc64__ ++ ret = ehdr.e_machine == EM_PPC64; ++#elif defined __powerpc__ ++ ret = ehdr.e_machine == EM_PPC; ++#elif defined __s390__ || defined __s390x__ ++ ret = ehdr.e_machine == EM_S390; ++#elif defined __x86_64__ ++ ret = ehdr.e_machine == EM_X86_64; ++#elif defined __sparc__ ++ if (sizeof (long) == 8) ++ ret = ehdr.e_machine == EM_SPARCV9; ++ else ++ ret = (ehdr.e_machine == EM_SPARC ++ || ehdr.e_machine == EM_SPARC32PLUS); ++#else ++ ret = 1; ++#endif ++ } ++ } ++ close (fd); ++ } ++ return ret; ++} diff --git a/glibc.spec b/glibc.spec index 6a75446..2610242 100644 --- a/glibc.spec +++ b/glibc.spec @@ -118,7 +118,6 @@ License: LGPLv2+ and LGPLv2+ with exceptions and GPLv2+ URL: http://www.gnu.org/software/glibc/ Source0: %{?glibc_release_url}%{glibcsrcdir}.tar.gz Source1: build-locale-archive.c -Source2: glibc_post_upgrade.c Source4: nscd.conf Source7: nsswitch.conf Source8: power6emul.c @@ -154,7 +153,8 @@ Source11: SUPPORTED # ############################################################################## -Patch0001: glibc-fedora-nscd.patch +Patch1: glibc-post_upgrade.patch +Patch2: glibc-fedora-nscd.patch # All these were from the glibc-fedora.patch mega-patch and need another # round of reviewing. Ideally they'll either be submitted upstream or @@ -734,7 +734,8 @@ microbenchmark tests on the system. %setup -q -n %{glibcsrcdir} # Patch order matters. -%patch0001 -p1 +%patch1 -p1 +%patch2 -p1 %patch2007 -p1 %patch0012 -p1 %patch2013 -p1 @@ -999,18 +1000,6 @@ build ) %endif -############################################################################## -# Build the glibc post-upgrade program: -# We only build one of these with the default set of options. This program -# must be able to run on all hardware for the lowest common denomintor since -# we only build it once. -############################################################################## -pushd build-%{target} -$GCC -static -L. -Os -g %{SOURCE2} \ - -o glibc_post_upgrade.%{_target_cpu} \ - '-DGCONV_MODULES_DIR="%{_libdir}/gconv"' -popd - ############################################################################## # Install glibc... ############################################################################## @@ -1245,7 +1234,7 @@ chmod 644 $RPM_BUILD_ROOT%{_libdir}/gconv/gconv-modules.cache ############################################################################## # Install the upgrade program -install -m 700 build-%{target}/glibc_post_upgrade.%{_target_cpu} \ +install -m 700 build-%{target}/elf/glibc_post_upgrade \ $RPM_BUILD_ROOT%{_prefix}/sbin/glibc_post_upgrade.%{_target_cpu} # Strip all of the installed object files. @@ -1494,7 +1483,7 @@ rm -rf $RPM_BUILD_ROOT%{_prefix}/share/zoneinfo # doesn't seem to be any macro to give us that. So we do the next best thing, # which is to at least keep the timestamp consistent. The choice of using # glibc_post_upgrade.c is arbitrary. -touch -r %{SOURCE2} $RPM_BUILD_ROOT/etc/ld.so.conf +touch -r %{SOURCE0} $RPM_BUILD_ROOT/etc/ld.so.conf touch -r sunrpc/etc.rpc $RPM_BUILD_ROOT/etc/rpc pushd build-%{target}