Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package patchelf for openSUSE:Factory checked in at 2021-10-19 23:03:33 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/patchelf (Old) and /work/SRC/openSUSE:Factory/.patchelf.new.1890 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "patchelf" Tue Oct 19 23:03:33 2021 rev:14 rq:925683 version:0.13 Changes: -------- --- /work/SRC/openSUSE:Factory/patchelf/patchelf.changes 2020-09-21 17:12:46.587426574 +0200 +++ /work/SRC/openSUSE:Factory/.patchelf.new.1890/patchelf.changes 2021-10-19 23:03:35.993266751 +0200 @@ -1,0 +2,15 @@ +Sat Oct 16 12:24:20 UTC 2021 - Dirk M??ller <dmuel...@suse.com> + +- update to 0.13: + * mark phdrs synced with sections, avoid rechecking it when syncing not + * fix roundUp() to not truncate 64 bit values + * Use sh_offset instead of sh_addr when checking already replaced libs + * Add ELF test case with invalid string table index + * Gracefully handle ELF files with invalid string table indices in header + * clang-tidy fixes + * avoid needless memory allocation when replacing sections + * silence compiler warning because of different signedness + * avoid needless copies of std::string + * patchelf: Fix alignment issues with contiguous note sections + +------------------------------------------------------------------- Old: ---- patchelf-0.12.tar.bz2 New: ---- patchelf-0.13.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ patchelf.spec ++++++ --- /var/tmp/diff_new_pack.ewfvxC/_old 2021-10-19 23:03:36.425266947 +0200 +++ /var/tmp/diff_new_pack.ewfvxC/_new 2021-10-19 23:03:36.425266947 +0200 @@ -1,7 +1,7 @@ # # spec file for package patchelf # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2021 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: patchelf -Version: 0.12 +Version: 0.13 Release: 0 Summary: A utility for patching ELF binaries License: GPL-3.0-only @@ -32,14 +32,14 @@ executables and change the RPATH of executables and libraries. %prep -%setup -q -n %{name}-0.12.20200827.8d3a16e +%setup -q -n %{name}-0.13.20210805.a949ff2 %build %configure -make %{?_smp_mflags} +%make_build %check -make %{?_smp_mflags} check +%make_build check %install %make_install ++++++ patchelf-0.12.tar.bz2 -> patchelf-0.13.tar.bz2 ++++++ ++++ 2097 lines of diff (skipped) ++++ retrying with extended exclude list diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/patchelf-0.12.20200827.8d3a16e/README.md new/patchelf-0.13.20210805.a949ff2/README.md --- old/patchelf-0.12.20200827.8d3a16e/README.md 1970-01-01 01:00:01.000000000 +0100 +++ new/patchelf-0.13.20210805.a949ff2/README.md 1970-01-01 01:00:01.000000000 +0100 @@ -99,6 +99,12 @@ ## Release History +0.13 (August 5, 2021): + +* New `--add-rpath` flag. + +* Bug fixes. + 0.12 (August 27, 2020): * New `--clear-symbol-version` flag. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/patchelf-0.12.20200827.8d3a16e/build-aux/compile new/patchelf-0.13.20210805.a949ff2/build-aux/compile --- old/patchelf-0.12.20200827.8d3a16e/build-aux/compile 2020-08-27 14:12:58.000000000 +0200 +++ new/patchelf-0.13.20210805.a949ff2/build-aux/compile 2021-08-05 14:55:32.000000000 +0200 @@ -3,7 +3,7 @@ scriptversion=2018-03-07.03; # UTC -# Copyright (C) 1999-2018 Free Software Foundation, Inc. +# Copyright (C) 1999-2020 Free Software Foundation, Inc. # Written by Tom Tromey <tro...@cygnus.com>. # # This program is free software; you can redistribute it and/or modify @@ -53,7 +53,7 @@ MINGW*) file_conv=mingw ;; - CYGWIN*) + CYGWIN* | MSYS*) file_conv=cygwin ;; *) @@ -67,7 +67,7 @@ mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; - cygwin/*) + cygwin/* | msys/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/patchelf-0.12.20200827.8d3a16e/build-aux/missing new/patchelf-0.13.20210805.a949ff2/build-aux/missing --- old/patchelf-0.12.20200827.8d3a16e/build-aux/missing 2020-08-27 14:12:58.000000000 +0200 +++ new/patchelf-0.13.20210805.a949ff2/build-aux/missing 2021-08-05 14:55:32.000000000 +0200 @@ -3,7 +3,7 @@ scriptversion=2018-03-07.03; # UTC -# Copyright (C) 1996-2018 Free Software Foundation, Inc. +# Copyright (C) 1996-2020 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard <pin...@iro.umontreal.ca>, 1996. # This program is free software; you can redistribute it and/or modify diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/patchelf-0.12.20200827.8d3a16e/build-aux/test-driver new/patchelf-0.13.20210805.a949ff2/build-aux/test-driver --- old/patchelf-0.12.20200827.8d3a16e/build-aux/test-driver 2020-08-27 14:12:59.000000000 +0200 +++ new/patchelf-0.13.20210805.a949ff2/build-aux/test-driver 2021-08-05 14:55:32.000000000 +0200 @@ -3,7 +3,7 @@ scriptversion=2018-03-07.03; # UTC -# Copyright (C) 2011-2018 Free Software Foundation, Inc. +# Copyright (C) 2011-2020 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/patchelf-0.12.20200827.8d3a16e/configure.ac new/patchelf-0.13.20210805.a949ff2/configure.ac --- old/patchelf-0.12.20200827.8d3a16e/configure.ac 1970-01-01 01:00:01.000000000 +0100 +++ new/patchelf-0.13.20210805.a949ff2/configure.ac 1970-01-01 01:00:01.000000000 +0100 @@ -6,6 +6,7 @@ AM_PROG_CC_C_O AC_PROG_CXX +AM_PROG_AS DEFAULT_PAGESIZE=auto AC_ARG_WITH([page-size], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/patchelf-0.12.20200827.8d3a16e/patchelf.1 new/patchelf-0.13.20210805.a949ff2/patchelf.1 --- old/patchelf-0.12.20200827.8d3a16e/patchelf.1 1970-01-01 01:00:01.000000000 +0100 +++ new/patchelf-0.13.20210805.a949ff2/patchelf.1 1970-01-01 01:00:01.000000000 +0100 @@ -43,6 +43,9 @@ .IP "--set-rpath RPATH" Change the RPATH of the executable or library to RPATH. +.IP "--add-rpath RPATH" +Add RPATH to the existing RPATH of the executable or library. + .IP --remove-rpath Removes the DT_RPATH or DT_RUNPATH entry of the executable or library. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/patchelf-0.12.20200827.8d3a16e/patchelf.spec new/patchelf-0.13.20210805.a949ff2/patchelf.spec --- old/patchelf-0.12.20200827.8d3a16e/patchelf.spec 2020-08-27 14:13:00.000000000 +0200 +++ new/patchelf-0.13.20210805.a949ff2/patchelf.spec 2021-08-05 14:55:33.000000000 +0200 @@ -1,7 +1,7 @@ Summary: A utility for patching ELF binaries Name: patchelf -Version: 0.12.20200827.8d3a16e +Version: 0.13.20210805.a949ff2 Release: 1 License: GPL Group: Development/Tools diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/patchelf-0.12.20200827.8d3a16e/src/elf.h new/patchelf-0.13.20210805.a949ff2/src/elf.h --- old/patchelf-0.12.20200827.8d3a16e/src/elf.h 1970-01-01 01:00:01.000000000 +0100 +++ new/patchelf-0.13.20210805.a949ff2/src/elf.h 1970-01-01 01:00:01.000000000 +0100 @@ -366,7 +366,9 @@ required */ #define SHF_GROUP (1 << 9) /* Section is member of a group. */ #define SHF_TLS (1 << 10) /* Section hold thread-local data. */ +#define SHF_COMPRESSED (1 << 11) /* Section with compressed data */ #define SHF_MASKOS 0x0ff00000 /* OS-specific. */ +#define SHF_GNU_BUILD_NOTE (1 << 20) /* Section contains GNU BUILD ATTRIBUTE notes. */ #define SHF_MASKPROC 0xf0000000 /* Processor-specific */ #define SHF_ORDERED (1 << 30) /* Special ordering requirement (Solaris). */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/patchelf-0.12.20200827.8d3a16e/src/patchelf.cc new/patchelf-0.13.20210805.a949ff2/src/patchelf.cc --- old/patchelf-0.12.20200827.8d3a16e/src/patchelf.cc 1970-01-01 01:00:01.000000000 +0100 +++ new/patchelf-0.13.20210805.a949ff2/src/patchelf.cc 1970-01-01 01:00:01.000000000 +0100 @@ -16,27 +16,27 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <string> -#include <vector> -#include <set> -#include <map> #include <algorithm> +#include <limits> +#include <map> #include <memory> +#include <set> #include <sstream> -#include <limits> #include <stdexcept> +#include <string> +#include <vector> -#include <cstdlib> -#include <cstdio> -#include <cstdarg> #include <cassert> -#include <cstring> #include <cerrno> +#include <cstdarg> +#include <cstdio> +#include <cstdlib> +#include <cstring> -#include <sys/types.h> +#include <fcntl.h> #include <sys/stat.h> +#include <sys/types.h> #include <unistd.h> -#include <fcntl.h> #include "elf.h" @@ -54,8 +54,7 @@ static int forcedPageSize = -1; #endif -typedef std::shared_ptr<std::vector<unsigned char>> FileContents; - +using FileContents = std::shared_ptr<std::vector<unsigned char>>; #define ElfFileParams class Elf_Ehdr, class Elf_Phdr, class Elf_Shdr, class Elf_Addr, class Elf_Off, class Elf_Dyn, class Elf_Sym, class Elf_Verneed, class Elf_Versym #define ElfFileParamNames Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Addr, Elf_Off, Elf_Dyn, Elf_Sym, Elf_Verneed, Elf_Versym @@ -79,9 +78,7 @@ static bool hasAllowedPrefix(const std::string & s, const std::vector<std::string> & allowedPrefixes) { - for (auto & i : allowedPrefixes) - if (!s.compare(0, i.size(), i)) return true; - return false; + return std::any_of(allowedPrefixes.begin(), allowedPrefixes.end(), [&](const std::string & i) { return !s.compare(0, i.size(), i); }); } @@ -106,8 +103,8 @@ bool isExecutable = false; - typedef std::string SectionName; - typedef std::map<SectionName, std::string> ReplacedSections; + using SectionName = std::string; + using ReplacedSections = std::map<SectionName, std::string>; ReplacedSections replacedSections; @@ -120,8 +117,7 @@ std::vector<SectionName> sectionsByOldIndex; public: - - ElfFile(FileContents fileContents); + explicit ElfFile(FileContents fileContents); bool isChanged() { @@ -136,8 +132,8 @@ bool operator ()(const Elf_Phdr & x, const Elf_Phdr & y) { // A PHDR comes before everything else. - if (y.p_type == PT_PHDR) return false; - if (x.p_type == PT_PHDR) return true; + if (elfFile->rdi(y.p_type) == PT_PHDR) return false; + if (elfFile->rdi(x.p_type) == PT_PHDR) return true; // Sort non-PHDRs by address. return elfFile->rdi(x.p_paddr) < elfFile->rdi(y.p_paddr); @@ -201,7 +197,7 @@ void setInterpreter(const std::string & newInterpreter); - typedef enum { rpPrint, rpShrink, rpSet, rpRemove } RPathOp; + typedef enum { rpPrint, rpShrink, rpSet, rpAdd, rpRemove } RPathOp; void modifyRPath(RPathOp op, const std::vector<std::string> & allowedRpathPrefixes, std::string newRPath); @@ -291,41 +287,37 @@ struct SysError : std::runtime_error { int errNo; - SysError(const std::string & msg) + explicit SysError(const std::string & msg) : std::runtime_error(fmt(msg + ": " + strerror(errno))) , errNo(errno) { } }; - -__attribute__((noreturn)) static void error(std::string msg) +__attribute__((noreturn)) static void error(const std::string & msg) { if (errno) throw SysError(msg); - else - throw std::runtime_error(msg); + throw std::runtime_error(msg); } - -static void growFile(FileContents contents, size_t newSize) +static void growFile(const FileContents & contents, size_t newSize) { if (newSize > contents->capacity()) error("maximum file size exceeded"); if (newSize <= contents->size()) return; contents->resize(newSize, 0); } - -static FileContents readFile(std::string fileName, +static FileContents readFile(const std::string & fileName, size_t cutOff = std::numeric_limits<size_t>::max()) { struct stat st; if (stat(fileName.c_str(), &st) != 0) throw SysError(fmt("getting info about '", fileName, "'")); - if ((uint64_t) st.st_size > (uint64_t) std::numeric_limits<size_t>::max()) + if (static_cast<uint64_t>(st.st_size) > static_cast<uint64_t>(std::numeric_limits<size_t>::max())) throw SysError(fmt("cannot read file of size ", st.st_size, " into memory")); - size_t size = std::min(cutOff, (size_t) st.st_size); + size_t size = std::min(cutOff, static_cast<size_t>(st.st_size)); FileContents contents = std::make_shared<std::vector<unsigned char>>(); contents->reserve(size + 32 * 1024 * 1024); @@ -358,7 +350,8 @@ ElfType getElfType(const FileContents & fileContents) { /* Check the ELF header for basic validity. */ - if (fileContents->size() < (off_t) sizeof(Elf32_Ehdr)) error("missing ELF header"); + if (fileContents->size() < static_cast<off_t>(sizeof(Elf32_Ehdr))) + error("missing ELF header"); auto contents = fileContents->data(); @@ -374,20 +367,21 @@ bool is32Bit = contents[EI_CLASS] == ELFCLASS32; // FIXME: endianness - return ElfType{is32Bit, is32Bit ? ((Elf32_Ehdr *) contents)->e_machine : ((Elf64_Ehdr *) contents)->e_machine}; + return ElfType { is32Bit, is32Bit ? (reinterpret_cast<Elf32_Ehdr *>(contents))->e_machine : (reinterpret_cast<Elf64_Ehdr *>(contents))->e_machine }; } static void checkPointer(const FileContents & contents, void * p, unsigned int size) { - unsigned char * q = (unsigned char *) p; - assert(q >= contents->data() && q + size <= contents->data() + contents->size()); + auto q = static_cast<unsigned char *>(p); + if (!(q >= contents->data() && q + size <= contents->data() + contents->size())) + error("data region extends past file end"); } template<ElfFileParams> -ElfFile<ElfFileParamNames>::ElfFile(FileContents fileContents) - : fileContents(fileContents) +ElfFile<ElfFileParamNames>::ElfFile(FileContents fContents) + : fileContents(fContents) , contents(fileContents->data()) { /* Check the ELF header for basic validity. */ @@ -403,13 +397,13 @@ if (rdi(hdr->e_type) != ET_EXEC && rdi(hdr->e_type) != ET_DYN) error("wrong ELF type"); - if ((size_t) (rdi(hdr->e_phoff) + rdi(hdr->e_phnum) * rdi(hdr->e_phentsize)) > fileContents->size()) + if (rdi(hdr->e_phoff) + size_t(rdi(hdr->e_phnum) * rdi(hdr->e_phentsize)) > fileContents->size()) error("program header table out of bounds"); if (rdi(hdr->e_shnum) == 0) error("no section headers. The input file is probably a statically linked, self-decompressing binary"); - if ((size_t) (rdi(hdr->e_shoff) + rdi(hdr->e_shnum) * rdi(hdr->e_shentsize)) > fileContents->size()) + if (rdi(hdr->e_shoff) + size_t(rdi(hdr->e_shnum) * rdi(hdr->e_shentsize)) > fileContents->size()) error("section header table out of bounds"); if (rdi(hdr->e_phentsize) != sizeof(Elf_Phdr)) @@ -417,29 +411,41 @@ /* Copy the program and section headers. */ for (int i = 0; i < rdi(hdr->e_phnum); ++i) { - phdrs.push_back(* ((Elf_Phdr *) (contents + rdi(hdr->e_phoff)) + i)); + Elf_Phdr *phdr = (Elf_Phdr *) (contents + rdi(hdr->e_phoff)) + i; + + checkPointer(fileContents, phdr, sizeof(*phdr)); + phdrs.push_back(*phdr); if (rdi(phdrs[i].p_type) == PT_INTERP) isExecutable = true; } - for (int i = 0; i < rdi(hdr->e_shnum); ++i) - shdrs.push_back(* ((Elf_Shdr *) (contents + rdi(hdr->e_shoff)) + i)); + for (int i = 0; i < rdi(hdr->e_shnum); ++i) { + Elf_Shdr *shdr = (Elf_Shdr *) (contents + rdi(hdr->e_shoff)) + i; + + checkPointer(fileContents, shdr, sizeof(*shdr)); + shdrs.push_back(*shdr); + } /* Get the section header string table section (".shstrtab"). Its index in the section header table is given by e_shstrndx field of the ELF header. */ unsigned int shstrtabIndex = rdi(hdr->e_shstrndx); - assert(shstrtabIndex < shdrs.size()); + if (shstrtabIndex >= shdrs.size()) + error("string table index out of bounds"); + unsigned int shstrtabSize = rdi(shdrs[shstrtabIndex].sh_size); char * shstrtab = (char * ) contents + rdi(shdrs[shstrtabIndex].sh_offset); checkPointer(fileContents, shstrtab, shstrtabSize); - assert(shstrtabSize > 0); - assert(shstrtab[shstrtabSize - 1] == 0); + if (shstrtabSize == 0) + error("string table size is zero"); + + if (shstrtab[shstrtabSize - 1] != 0) + error("string table is not zero terminated"); sectionNames = std::string(shstrtab, shstrtabSize); - sectionsByOldIndex.resize(hdr->e_shnum); - for (unsigned int i = 1; i < rdi(hdr->e_shnum); ++i) + sectionsByOldIndex.resize(shdrs.size()); + for (size_t i = 1; i < shdrs.size(); ++i) sectionsByOldIndex[i] = getSectionName(shdrs[i]); } @@ -453,7 +459,7 @@ // Architectures (and ABIs) can have different minimum section alignment // requirements. There is no authoritative list of these values. The // current list is extracted from GNU gold's source code (abi_pagesize). - switch (hdr->e_machine) { + switch (rdi(hdr->e_machine)) { case EM_SPARC: case EM_MIPS: case EM_PPC: @@ -519,8 +525,7 @@ wri(hdr->e_shstrndx, findSection3(shstrtabName)); } - -static void writeFile(std::string fileName, FileContents contents) +static void writeFile(const std::string & fileName, const FileContents & contents) { debug("writing %s\n", fileName.c_str()); @@ -541,7 +546,7 @@ } -static unsigned int roundUp(unsigned int n, unsigned int m) +static uint64_t roundUp(uint64_t n, uint64_t m) { return ((n - 1) / m + 1) * m; } @@ -594,7 +599,12 @@ template<ElfFileParams> std::string ElfFile<ElfFileParamNames>::getSectionName(const Elf_Shdr & shdr) const { - return std::string(sectionNames.c_str() + rdi(shdr.sh_name)); + const size_t name_off = rdi(shdr.sh_name); + + if (name_off >= sectionNames.size()) + error("section name offset out of bounds"); + + return std::string(sectionNames.c_str() + name_off); } @@ -603,7 +613,7 @@ { auto shdr = findSection2(sectionName); if (!shdr) { - std::string extraMsg = ""; + std::string extraMsg; if (sectionName == ".interp" || sectionName == ".dynamic" || sectionName == ".dynstr") extraMsg = ". The input file is most likely statically linked"; error("cannot find section '" + sectionName + "'" + extraMsg); @@ -616,7 +626,7 @@ Elf_Shdr * ElfFile<ElfFileParamNames>::findSection2(const SectionName & sectionName) { auto i = findSection3(sectionName); - return i ? &shdrs[i] : 0; + return i ? &shdrs[i] : nullptr; } @@ -631,14 +641,14 @@ template<ElfFileParams> bool ElfFile<ElfFileParamNames>::haveReplacedSection(const SectionName & sectionName) const { - return (replacedSections.find(sectionName) != replacedSections.end()); + return replacedSections.count(sectionName); } template<ElfFileParams> std::string & ElfFile<ElfFileParamNames>::replaceSection(const SectionName & sectionName, unsigned int size) { - ReplacedSections::iterator i = replacedSections.find(sectionName); + auto i = replacedSections.find(sectionName); std::string s; if (i != replacedSections.end()) { @@ -663,14 +673,15 @@ *before* writing the new section contents (below) to prevent clobbering previously written new section contents. */ for (auto & i : replacedSections) { - std::string sectionName = i.first; + const std::string & sectionName = i.first; Elf_Shdr & shdr = findSection(sectionName); - if (shdr.sh_type != SHT_NOBITS) + if (rdi(shdr.sh_type) != SHT_NOBITS) memset(contents + rdi(shdr.sh_offset), 'X', rdi(shdr.sh_size)); } + std::set<unsigned int> noted_phdrs = {}; for (auto & i : replacedSections) { - std::string sectionName = i.first; + const std::string & sectionName = i.first; auto & shdr = findSection(sectionName); Elf_Shdr orig_shdr = shdr; debug("rewriting section '%s' from offset 0x%x (size %d) to offset 0x%x (size %d)\n", @@ -688,23 +699,25 @@ /* If this is the .interp section, then the PT_INTERP segment must be sync'ed with it. */ if (sectionName == ".interp") { - for (unsigned int j = 0; j < phdrs.size(); ++j) - if (rdi(phdrs[j].p_type) == PT_INTERP) { - phdrs[j].p_offset = shdr.sh_offset; - phdrs[j].p_vaddr = phdrs[j].p_paddr = shdr.sh_addr; - phdrs[j].p_filesz = phdrs[j].p_memsz = shdr.sh_size; + for (auto & phdr : phdrs) { + if (rdi(phdr.p_type) == PT_INTERP) { + phdr.p_offset = shdr.sh_offset; + phdr.p_vaddr = phdr.p_paddr = shdr.sh_addr; + phdr.p_filesz = phdr.p_memsz = shdr.sh_size; } + } } /* If this is the .dynamic section, then the PT_DYNAMIC segment must be sync'ed with it. */ - if (sectionName == ".dynamic") { - for (unsigned int j = 0; j < phdrs.size(); ++j) - if (rdi(phdrs[j].p_type) == PT_DYNAMIC) { - phdrs[j].p_offset = shdr.sh_offset; - phdrs[j].p_vaddr = phdrs[j].p_paddr = shdr.sh_addr; - phdrs[j].p_filesz = phdrs[j].p_memsz = shdr.sh_size; + else if (sectionName == ".dynamic") { + for (auto & phdr : phdrs) { + if (rdi(phdr.p_type) == PT_DYNAMIC) { + phdr.p_offset = shdr.sh_offset; + phdr.p_vaddr = phdr.p_paddr = shdr.sh_addr; + phdr.p_filesz = phdr.p_memsz = shdr.sh_size; } + } } /* If this is a note section, there might be a PT_NOTE segment that @@ -721,7 +734,7 @@ shdr.sh_addralign = orig_shdr.sh_addralign; for (unsigned int j = 0; j < phdrs.size(); ++j) - if (rdi(phdrs[j].p_type) == PT_NOTE) { + if (rdi(phdrs[j].p_type) == PT_NOTE && !noted_phdrs.count(j)) { Elf_Off p_start = rdi(phdrs[j].p_offset); Elf_Off p_end = p_start + rdi(phdrs[j].p_filesz); Elf_Off s_start = rdi(orig_shdr.sh_offset); @@ -739,6 +752,8 @@ phdrs[j].p_offset = shdr.sh_offset; phdrs[j].p_vaddr = phdrs[j].p_paddr = shdr.sh_addr; phdrs[j].p_filesz = phdrs[j].p_memsz = shdr.sh_size; + + noted_phdrs.insert(j); } } @@ -757,19 +772,20 @@ PT_LOAD segment located directly after the last virtual address page of other segments. */ Elf_Addr startPage = 0; - for (unsigned int i = 0; i < phdrs.size(); ++i) { - Elf_Addr thisPage = roundUp(rdi(phdrs[i].p_vaddr) + rdi(phdrs[i].p_memsz), getPageSize()); + Elf_Addr firstPage = 0; + for (auto & phdr : phdrs) { + Elf_Addr thisPage = roundUp(rdi(phdr.p_vaddr) + rdi(phdr.p_memsz), getPageSize()); if (thisPage > startPage) startPage = thisPage; + if (rdi(phdr.p_type) == PT_PHDR) firstPage = rdi(phdr.p_vaddr) - rdi(phdr.p_offset); } debug("last page is 0x%llx\n", (unsigned long long) startPage); + debug("first page is 0x%llx\n", (unsigned long long) firstPage); /* When normalizing note segments we will in the worst case be adding 1 program header for each SHT_NOTE section. */ - unsigned int num_notes = 0; - for (const auto & shdr : shdrs) - if (rdi(shdr.sh_type) == SHT_NOTE) - num_notes++; + unsigned int num_notes = std::count_if(shdrs.begin(), shdrs.end(), + [this](Elf_Shdr shdr) { return rdi(shdr.sh_type) == SHT_NOTE; }); /* Because we're adding a new section header, we're necessarily increasing the size of the program header table. This can cause the first section @@ -778,19 +794,19 @@ /* Some sections may already be replaced so account for that */ unsigned int i = 1; Elf_Addr pht_size = sizeof(Elf_Ehdr) + (phdrs.size() + num_notes + 1)*sizeof(Elf_Phdr); - while( shdrs[i].sh_addr <= pht_size && i < rdi(hdr->e_shnum) ) { + while( rdi(shdrs[i].sh_offset) <= pht_size && i < rdi(hdr->e_shnum) ) { if (not haveReplacedSection(getSectionName(shdrs[i]))) - replaceSection(getSectionName(shdrs[i]), shdrs[i].sh_size); + replaceSection(getSectionName(shdrs[i]), rdi(shdrs[i].sh_size)); i++; } /* Compute the total space needed for the replaced sections */ off_t neededSpace = 0; - for (auto & i : replacedSections) - neededSpace += roundUp(i.second.size(), sectionAlignment); + for (auto & s : replacedSections) + neededSpace += roundUp(s.second.size(), sectionAlignment); debug("needed space is %d\n", neededSpace); - size_t startOffset = roundUp(fileContents->size(), getPageSize()); + Elf_Off startOffset = roundUp(fileContents->size(), getPageSize()); growFile(fileContents, startOffset + neededSpace); @@ -835,7 +851,7 @@ assert(curOff == startOffset + neededSpace); /* Write out the updated program and section headers */ - rewriteHeaders(hdr->e_phoff); + rewriteHeaders(firstPage + rdi(hdr->e_phoff)); } @@ -851,7 +867,7 @@ unsigned int lastReplaced = 0; for (unsigned int i = 1; i < rdi(hdr->e_shnum); ++i) { std::string sectionName = getSectionName(shdrs[i]); - if (replacedSections.find(sectionName) != replacedSections.end()) { + if (replacedSections.count(sectionName)) { debug("using replaced section '%s'\n", sectionName.c_str()); lastReplaced = i; } @@ -882,13 +898,12 @@ startAddr = rdi(shdr.sh_addr); lastReplaced = i - 1; break; - } else { - if (replacedSections.find(sectionName) == replacedSections.end()) { - debug("replacing section '%s' which is in the way\n", sectionName.c_str()); - replaceSection(sectionName, rdi(shdr.sh_size)); - } } - prevSection = sectionName; + if (!replacedSections.count(sectionName)) { + debug("replacing section '%s' which is in the way\n", sectionName.c_str()); + replaceSection(sectionName, rdi(shdr.sh_size)); + } + prevSection = std::move(sectionName); } debug("first reserved offset/addr is 0x%x/0x%llx\n", @@ -971,16 +986,11 @@ one of them has to be replaced. */ /* We don't need to do anything if no note segments were replaced. */ - bool replaced_note = false; - for (const auto & i : replacedSections) { - if (rdi(findSection(i.first).sh_type) == SHT_NOTE) - replaced_note = true; - } + bool replaced_note = std::any_of(replacedSections.begin(), replacedSections.end(), + [this](std::pair<const std::string, std::string> & i) { return rdi(findSection(i.first).sh_type) == SHT_NOTE; }); if (!replaced_note) return; - size_t orig_count = phdrs.size(); - for (size_t i = 0; i < orig_count; ++i) { - auto & phdr = phdrs[i]; + for (auto & phdr : phdrs) { if (rdi(phdr.p_type) != PT_NOTE) continue; size_t start_off = rdi(phdr.p_offset); @@ -993,8 +1003,9 @@ size_t size = 0; for (const auto & shdr : shdrs) { if (rdi(shdr.sh_type) != SHT_NOTE) continue; - if (rdi(shdr.sh_offset) != curr_off) continue; + if (rdi(shdr.sh_offset) != roundUp(curr_off, rdi(shdr.sh_addralign))) continue; size = rdi(shdr.sh_size); + curr_off = roundUp(curr_off, rdi(shdr.sh_addralign)); break; } if (size == 0) @@ -1050,11 +1061,11 @@ /* If there is a segment for the program header table, update it. (According to the ELF spec, there can only be one.) */ - for (unsigned int i = 0; i < phdrs.size(); ++i) { - if (rdi(phdrs[i].p_type) == PT_PHDR) { - phdrs[i].p_offset = hdr->e_phoff; - wri(phdrs[i].p_vaddr, wri(phdrs[i].p_paddr, phdrAddress)); - wri(phdrs[i].p_filesz, wri(phdrs[i].p_memsz, phdrs.size() * sizeof(Elf_Phdr))); + for (auto & phdr : phdrs) { + if (rdi(phdr.p_type) == PT_PHDR) { + phdr.p_offset = hdr->e_phoff; + wri(phdr.p_vaddr, wri(phdr.p_paddr, phdrAddress)); + wri(phdr.p_filesz, wri(phdr.p_memsz, phdrs.size() * sizeof(Elf_Phdr))); break; } } @@ -1078,7 +1089,7 @@ (e.g., those produced by klibc's klcc). */ auto shdrDynamic = findSection2(".dynamic"); if (shdrDynamic) { - Elf_Dyn * dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic->sh_offset)); + auto dyn = (Elf_Dyn *)(contents + rdi(shdrDynamic->sh_offset)); unsigned int d_tag; for ( ; (d_tag = rdi(dyn->d_tag)) != DT_NULL; dyn++) if (d_tag == DT_STRTAB) @@ -1129,14 +1140,14 @@ if (rdi(shdrs[i].sh_type) != SHT_SYMTAB && rdi(shdrs[i].sh_type) != SHT_DYNSYM) continue; debug("rewriting symbol table section %d\n", i); for (size_t entry = 0; (entry + 1) * sizeof(Elf_Sym) <= rdi(shdrs[i].sh_size); entry++) { - Elf_Sym * sym = (Elf_Sym *) (contents + rdi(shdrs[i].sh_offset) + entry * sizeof(Elf_Sym)); + auto sym = (Elf_Sym *)(contents + rdi(shdrs[i].sh_offset) + entry * sizeof(Elf_Sym)); unsigned int shndx = rdi(sym->st_shndx); if (shndx != SHN_UNDEF && shndx < SHN_LORESERVE) { if (shndx >= sectionsByOldIndex.size()) { fprintf(stderr, "warning: entry %d in symbol table refers to a non-existent section, skipping\n", shndx); continue; } - std::string section = sectionsByOldIndex.at(shndx); + const std::string & section = sectionsByOldIndex.at(shndx); assert(!section.empty()); auto newIndex = findSection3(section); // inefficient //debug("rewriting symbol %d: index = %d (%s) -> %d\n", entry, shndx, section.c_str(), newIndex); @@ -1179,9 +1190,9 @@ char * strTab = (char *) contents + rdi(shdrDynStr.sh_offset); /* Walk through the dynamic section, look for the DT_SONAME entry. */ - Elf_Dyn * dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset)); - Elf_Dyn * dynSoname = 0; - char * soname = 0; + auto dyn = (Elf_Dyn *)(contents + rdi(shdrDynamic.sh_offset)); + Elf_Dyn * dynSoname = nullptr; + char * soname = nullptr; for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) { if (rdi(dyn->d_tag) == DT_SONAME) { dynSoname = dyn; @@ -1191,7 +1202,7 @@ if (op == printSoname) { if (soname) { - if (std::string(soname ? soname : "") == "") + if (strlen(soname) == 0) debug("DT_SONAME is empty\n"); else printf("%s\n", soname); @@ -1201,7 +1212,7 @@ return; } - if (std::string(soname ? soname : "") == newSoname) { + if (soname && soname == newSoname) { debug("current and proposed new SONAMEs are equal keeping DT_SONAME entry\n"); return; } @@ -1280,9 +1291,9 @@ generates a DT_RPATH and DT_RUNPATH pointing at the same string. */ std::vector<std::string> neededLibs; - Elf_Dyn * dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset)); - Elf_Dyn * dynRPath = 0, * dynRunPath = 0; - char * rpath = 0; + auto dyn = (Elf_Dyn *)(contents + rdi(shdrDynamic.sh_offset)); + Elf_Dyn *dynRPath = nullptr, *dynRunPath = nullptr; + char * rpath = nullptr; for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) { if (rdi(dyn->d_tag) == DT_RPATH) { dynRPath = dyn; @@ -1339,7 +1350,8 @@ if (!neededLibFound[j]) { std::string libName = dirName + "/" + neededLibs[j]; try { - if (getElfType(readFile(libName, sizeof(Elf32_Ehdr))).machine == rdi(hdr->e_machine)) { + Elf32_Half library_e_machine = getElfType(readFile(libName, sizeof(Elf32_Ehdr))).machine; + if (rdi(library_e_machine) == rdi(hdr->e_machine)) { neededLibFound[j] = true; libFound = true; } else @@ -1362,7 +1374,7 @@ return; } - Elf_Dyn * dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset)); + dyn = (Elf_Dyn *)(contents + rdi(shdrDynamic.sh_offset)); Elf_Dyn * last = dyn; for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) { if (rdi(dyn->d_tag) == DT_RPATH) { @@ -1383,19 +1395,23 @@ if (!forceRPath && dynRPath && !dynRunPath) { /* convert DT_RPATH to DT_RUNPATH */ wri(dynRPath->d_tag, DT_RUNPATH); dynRunPath = dynRPath; - dynRPath = 0; + dynRPath = nullptr; changed = true; } else if (forceRPath && dynRunPath) { /* convert DT_RUNPATH to DT_RPATH */ wri(dynRunPath->d_tag, DT_RPATH); dynRPath = dynRunPath; - dynRunPath = 0; + dynRunPath = nullptr; changed = true; } - if (std::string(rpath ? rpath : "") == newRPath) { + if (rpath && rpath == newRPath) { return; } + if (op == rpAdd) { + newRPath = std::string(rpath ? rpath : "") + ":" + newRPath; + } + changed = true; /* Zero out the previous rpath to prevent retained dependencies in @@ -1459,12 +1475,12 @@ auto shdrDynStr = findSection(".dynstr"); char * strTab = (char *) contents + rdi(shdrDynStr.sh_offset); - Elf_Dyn * dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset)); + auto dyn = (Elf_Dyn *)(contents + rdi(shdrDynamic.sh_offset)); Elf_Dyn * last = dyn; for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) { if (rdi(dyn->d_tag) == DT_NEEDED) { char * name = strTab + rdi(dyn->d_un.d_val); - if (libs.find(name) != libs.end()) { + if (libs.count(name)) { debug("removing DT_NEEDED entry '%s'\n", name); changed = true; } else { @@ -1487,7 +1503,7 @@ auto shdrDynStr = findSection(".dynstr"); char * strTab = (char *) contents + rdi(shdrDynStr.sh_offset); - Elf_Dyn * dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset)); + auto dyn = (Elf_Dyn *)(contents + rdi(shdrDynamic.sh_offset)); unsigned int verNeedNum = 0; @@ -1497,7 +1513,7 @@ if (rdi(dyn->d_tag) == DT_NEEDED) { char * name = strTab + rdi(dyn->d_un.d_val); auto i = libs.find(name); - if (i != libs.end()) { + if (i != libs.end() && name != i->second) { auto replacement = i->second; debug("replacing DT_NEEDED entry '%s' with '%s'\n", name, replacement.c_str()); @@ -1545,11 +1561,11 @@ unsigned int verStrAddedBytes = 0; - Elf_Verneed * need = (Elf_Verneed *) (contents + rdi(shdrVersionR.sh_offset)); + auto need = (Elf_Verneed *)(contents + rdi(shdrVersionR.sh_offset)); while (verNeedNum > 0) { char * file = verStrTab + rdi(need->vn_file); auto i = libs.find(file); - if (i != libs.end()) { + if (i != libs.end() && file != i->second) { auto replacement = i->second; debug("replacing .gnu.version_r entry '%s' with '%s'\n", file, replacement.c_str()); @@ -1582,12 +1598,15 @@ auto shdrDynamic = findSection(".dynamic"); auto shdrDynStr = findSection(".dynstr"); - /* add all new libs to the dynstr string table */ unsigned int length = 0; - for (auto & i : libs) length += i.size() + 1; + + /* add all new libs to the dynstr string table */ + for (auto &lib : libs) + length += lib.size() + 1; std::string & newDynStr = replaceSection(".dynstr", rdi(shdrDynStr.sh_size) + length + 1); + std::set<Elf64_Xword> libStrings; unsigned int pos = 0; for (auto & i : libs) { @@ -1644,8 +1663,8 @@ { auto shdrDynamic = findSection(".dynamic"); - Elf_Dyn * dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset)); - Elf_Dyn * dynFlags1 = 0; + auto dyn = (Elf_Dyn *)(contents + rdi(shdrDynamic.sh_offset)); + auto dynFlags1 = (Elf_Dyn *)nullptr; for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) { if (rdi(dyn->d_tag) == DT_FLAGS_1) { dynFlags1 = dyn; @@ -1687,9 +1706,9 @@ auto shdrDynsym = findSection(".dynsym"); auto shdrVersym = findSection(".gnu.version"); - char * strTab = (char *) contents + rdi(shdrDynStr.sh_offset); - Elf_Sym * dynsyms = (Elf_Sym *) (contents + rdi(shdrDynsym.sh_offset)); - Elf_Versym * versyms = (Elf_Versym *) (contents + rdi(shdrVersym.sh_offset)); + auto strTab = (char *)contents + rdi(shdrDynStr.sh_offset); + auto dynsyms = (Elf_Sym *)(contents + rdi(shdrDynsym.sh_offset)); + auto versyms = (Elf_Versym *)(contents + rdi(shdrVersym.sh_offset)); size_t count = rdi(shdrDynsym.sh_size) / sizeof(Elf_Sym); if (count != rdi(shdrVersym.sh_size) / sizeof(Elf_Versym)) @@ -1698,7 +1717,7 @@ for (size_t i = 0; i < count; i++) { auto dynsym = dynsyms[i]; auto name = strTab + rdi(dynsym.st_name); - if (syms.find(name) != syms.end()) { + if (syms.count(name)) { debug("clearing symbol version for %s\n", name); wri(versyms[i], 1); } @@ -1715,6 +1734,7 @@ static std::vector<std::string> allowedRpathPrefixes; static bool removeRPath = false; static bool setRPath = false; +static bool addRPath = false; static bool printRPath = false; static std::string newRPath; static std::set<std::string> neededLibsToRemove; @@ -1725,7 +1745,7 @@ static bool noDefaultLib = false; template<class ElfFile> -static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, std::string fileName) +static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, const std::string & fileName) { if (printInterpreter) printf("%s\n", elfFile.getInterpreter().c_str()); @@ -1736,7 +1756,7 @@ if (setSoname) elfFile.modifySoname(elfFile.replaceSoname, newSoname); - if (newInterpreter != "") + if (!newInterpreter.empty()) elfFile.setInterpreter(newInterpreter); if (printRPath) @@ -1748,6 +1768,8 @@ elfFile.modifyRPath(elfFile.rpRemove, {}, ""); else if (setRPath) elfFile.modifyRPath(elfFile.rpSet, {}, newRPath); + else if (addRPath) + elfFile.modifyRPath(elfFile.rpAdd, {}, newRPath); if (printNeeded) elfFile.printNeededLibs(); @@ -1771,12 +1793,12 @@ static void patchElf() { - for (auto fileName : fileNames) { + for (const auto & fileName : fileNames) { if (!printInterpreter && !printRPath && !printSoname && !printNeeded) debug("patching ELF file '%s'\n", fileName.c_str()); auto fileContents = readFile(fileName); - std::string outputFileName2 = outputFileName.empty() ? fileName : outputFileName; + const std::string & outputFileName2 = outputFileName.empty() ? fileName : outputFileName; if (getElfType(fileContents).is32Bit) patchElf2(ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed, Elf32_Versym>(fileContents), fileContents, outputFileName2); @@ -1795,6 +1817,7 @@ [--print-soname]\t\tPrints 'DT_SONAME' entry of .dynamic section. Raises an error if DT_SONAME doesn't exist\n\ [--set-soname SONAME]\t\tSets 'DT_SONAME' entry to SONAME.\n\ [--set-rpath RPATH]\n\ + [--add-rpath RPATH]\n\ [--remove-rpath]\n\ [--shrink-rpath]\n\ [--allowed-rpath-prefixes PREFIXES]\t\tWith '--shrink-rpath', reject rpath entries not starting with the allowed prefix\n\ @@ -1820,7 +1843,8 @@ return 1; } - if (getenv("PATCHELF_DEBUG") != 0) debugMode = true; + if (getenv("PATCHELF_DEBUG") != nullptr) + debugMode = true; int i; for (i = 1; i < argc; ++i) { @@ -1860,6 +1884,11 @@ setRPath = true; newRPath = argv[i]; } + else if (arg == "--add-rpath") { + if (++i == argc) error("missing argument"); + addRPath = true; + newRPath = argv[i]; + } else if (arg == "--print-rpath") { printRPath = true; } @@ -1926,6 +1955,9 @@ if (!outputFileName.empty() && fileNames.size() != 1) error("--output option only allowed with single input file"); + if (setRPath && addRPath) + error("--set-rpath option not allowed with --add-rpath"); + patchElf(); return 0; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/patchelf-0.12.20200827.8d3a16e/tests/Makefile.am new/patchelf-0.13.20210805.a949ff2/tests/Makefile.am --- old/patchelf-0.12.20200827.8d3a16e/tests/Makefile.am 1970-01-01 01:00:01.000000000 +0100 +++ new/patchelf-0.13.20210805.a949ff2/tests/Makefile.am 1970-01-01 01:00:01.000000000 +0100 @@ -1,6 +1,6 @@ LIBS = -check_PROGRAMS = simple main main-scoped big-dynstr no-rpath +check_PROGRAMS = simple main main-scoped big-dynstr no-rpath contiguous_note_sections no_rpath_arch_TESTS = \ no-rpath-amd64.sh \ @@ -20,19 +20,23 @@ src_TESTS = \ plain-fail.sh plain-run.sh shrink-rpath.sh set-interpreter-short.sh \ - set-interpreter-long.sh set-rpath.sh no-rpath.sh big-dynstr.sh \ + set-interpreter-long.sh set-rpath.sh add-rpath.sh no-rpath.sh big-dynstr.sh \ set-rpath-library.sh soname.sh shrink-rpath-with-allowed-prefixes.sh \ force-rpath.sh \ plain-needed.sh \ output-flag.sh \ - build-id.sh + no-rpath-pie-powerpc.sh \ + build-id.sh \ + invalid-elf.sh \ + endianness.sh \ + contiguous_note_sections.sh build_TESTS = \ $(no_rpath_arch_TESTS) TESTS = $(src_TESTS) $(build_TESTS) -EXTRA_DIST = no-rpath-prebuild $(src_TESTS) no-rpath-prebuild.sh +EXTRA_DIST = no-rpath-prebuild $(src_TESTS) no-rpath-prebuild.sh invalid-elf endianness TESTS_ENVIRONMENT = PATCHELF_DEBUG=1 @@ -104,3 +108,7 @@ no_rpath_SOURCES = no-rpath.c # no -fpic for no-rpath.o no_rpath_CFLAGS = + +contiguous_note_sections_SOURCES = contiguous_note_sections.s contiguous_note_sections.ld +contiguous_note_sections_LDFLAGS = -nostdlib -T contiguous_note_sections.ld +contiguous_note_sections_CFLAGS = -pie diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/patchelf-0.12.20200827.8d3a16e/tests/add-rpath.sh new/patchelf-0.13.20210805.a949ff2/tests/add-rpath.sh --- old/patchelf-0.12.20200827.8d3a16e/tests/add-rpath.sh 1970-01-01 01:00:00.000000000 +0100 +++ new/patchelf-0.13.20210805.a949ff2/tests/add-rpath.sh 1970-01-01 01:00:01.000000000 +0100 @@ -0,0 +1,26 @@ +#! /bin/sh -e +SCRATCH=scratch/$(basename $0 .sh) + +rm -rf ${SCRATCH} +mkdir -p ${SCRATCH} +mkdir -p ${SCRATCH}/libsA +mkdir -p ${SCRATCH}/libsB + +cp main ${SCRATCH}/ +cp libfoo.so ${SCRATCH}/libsA/ +cp libbar.so ${SCRATCH}/libsB/ + +../src/patchelf --force-rpath --add-rpath $(pwd)/${SCRATCH}/libsA ${SCRATCH}/main +../src/patchelf --force-rpath --add-rpath $(pwd)/${SCRATCH}/libsB ${SCRATCH}/main + +if test "$(uname)" = FreeBSD; then + export LD_LIBRARY_PATH=$(pwd)/${SCRATCH}/libsB +fi + +exitCode=0 +(cd ${SCRATCH} && ./main) || exitCode=$? + +if test "$exitCode" != 46; then + echo "bad exit code!" + exit 1 +fi diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/patchelf-0.12.20200827.8d3a16e/tests/contiguous_note_sections.ld new/patchelf-0.13.20210805.a949ff2/tests/contiguous_note_sections.ld --- old/patchelf-0.12.20200827.8d3a16e/tests/contiguous_note_sections.ld 1970-01-01 01:00:00.000000000 +0100 +++ new/patchelf-0.13.20210805.a949ff2/tests/contiguous_note_sections.ld 1970-01-01 01:00:01.000000000 +0100 @@ -0,0 +1,24 @@ +PHDRS +{ + headers PT_PHDR PHDRS ; + notes PT_NOTE; + text PT_LOAD FILEHDR PHDRS ; + data PT_LOAD ; + interp PT_INTERP ; + dynamic PT_DYNAMIC ; +} + +SECTIONS +{ + . = SIZEOF_HEADERS; + . = ALIGN(4); + + .note.my-section0 : { *(.note.my-section0) } :notes :text + .note.my-section1 : { *(.note.my-section1) } :notes :text + + .interp : { *(.interp) } :text :interp + .text : { *(.text) } :text + .rodata : { *(.rodata) } /* defaults to :text */ + + .data : { *(.data) } :data +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/patchelf-0.12.20200827.8d3a16e/tests/contiguous_note_sections.s new/patchelf-0.13.20210805.a949ff2/tests/contiguous_note_sections.s --- old/patchelf-0.12.20200827.8d3a16e/tests/contiguous_note_sections.s 1970-01-01 01:00:00.000000000 +0100 +++ new/patchelf-0.13.20210805.a949ff2/tests/contiguous_note_sections.s 1970-01-01 01:00:01.000000000 +0100 @@ -0,0 +1,23 @@ +/* + * Testcase for error: + * patchelf: cannot normalize PT_NOTE segment: non-contiguous SHT_NOTE sections + */ +.section ".note.my-section0", "a", @note + .align 4 + .long 1f - 0f /* name length (not including padding) */ + .long 3f - 2f /* desc length (not including padding) */ + .long 1 /* type = NT_VERSION */ +0: .asciz "my-version-12345" /* name */ +1: .align 4 +2: .long 1 /* desc - toolchain version number, 32-bit LE */ +3: .align 4 + +.section ".note.my-section1", "a", @note + .align 8 + .long 1f - 0f /* name length (not including padding) */ + .long 3f - 2f /* desc length (not including padding) */ + .long 1 /* type = NT_VERSION */ +0: .asciz "my-version-1" /* name */ +1: .align 4 +2: .long 1 /* desc - toolchain version number, 32-bit LE */ +3: .align 4 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/patchelf-0.12.20200827.8d3a16e/tests/contiguous_note_sections.sh new/patchelf-0.13.20210805.a949ff2/tests/contiguous_note_sections.sh --- old/patchelf-0.12.20200827.8d3a16e/tests/contiguous_note_sections.sh 1970-01-01 01:00:00.000000000 +0100 +++ new/patchelf-0.13.20210805.a949ff2/tests/contiguous_note_sections.sh 1970-01-01 01:00:01.000000000 +0100 @@ -0,0 +1,6 @@ +#! /bin/sh -e + +# Running --set-interpreter on this binary should not produce the following +# error: +# patchelf: cannot normalize PT_NOTE segment: non-contiguous SHT_NOTE sections +../src/patchelf --set-interpreter ld-linux-x86-64.so.2 contiguous_note_sections diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/patchelf-0.12.20200827.8d3a16e/tests/endianness/Makefile new/patchelf-0.13.20210805.a949ff2/tests/endianness/Makefile --- old/patchelf-0.12.20200827.8d3a16e/tests/endianness/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ new/patchelf-0.13.20210805.a949ff2/tests/endianness/Makefile 1970-01-01 01:00:01.000000000 +0100 @@ -0,0 +1,3 @@ +all: + $(CC) -xc -nostdlib -shared -o libtest.so - <<< 'void test() {}' + $(CC) -xc -nostdlib -L. -ltest -o main - <<< 'void _start() {}' Binary files old/patchelf-0.12.20200827.8d3a16e/tests/endianness/ppc64/libtest.so and new/patchelf-0.13.20210805.a949ff2/tests/endianness/ppc64/libtest.so differ Binary files old/patchelf-0.12.20200827.8d3a16e/tests/endianness/ppc64/main and new/patchelf-0.13.20210805.a949ff2/tests/endianness/ppc64/main differ Binary files old/patchelf-0.12.20200827.8d3a16e/tests/endianness/ppc64le/libtest.so and new/patchelf-0.13.20210805.a949ff2/tests/endianness/ppc64le/libtest.so differ Binary files old/patchelf-0.12.20200827.8d3a16e/tests/endianness/ppc64le/main and new/patchelf-0.13.20210805.a949ff2/tests/endianness/ppc64le/main differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/patchelf-0.12.20200827.8d3a16e/tests/endianness.sh new/patchelf-0.13.20210805.a949ff2/tests/endianness.sh --- old/patchelf-0.12.20200827.8d3a16e/tests/endianness.sh 1970-01-01 01:00:00.000000000 +0100 +++ new/patchelf-0.13.20210805.a949ff2/tests/endianness.sh 1970-01-01 01:00:01.000000000 +0100 @@ -0,0 +1,24 @@ +#! /bin/sh -e +SCRATCH=scratch/$(basename $0 .sh) +PATCHELF="../src/patchelf" + +for arch in ppc64 ppc64le; do + rm -rf ${SCRATCH} + mkdir -p ${SCRATCH} + + cp endianness/${arch}/main endianness/${arch}/libtest.so ${SCRATCH}/ + + rpath="${PWD}/${SCRATCH}" + + # set rpath to scratch dir + ${PATCHELF} --output ${SCRATCH}/main-rpath --set-rpath $rpath ${SCRATCH}/main + + # attempt to shrink rpath, should not result in empty rpath + ${PATCHELF} --output ${SCRATCH}/main-shrunk --shrink-rpath --debug ${SCRATCH}/main-rpath + + # check whether rpath is still present + if ! ${PATCHELF} --print-rpath ${SCRATCH}/main-shrunk | grep -q "$rpath"; then + echo "rpath was removed for ${arch}" + exit 1 + fi +done Binary files old/patchelf-0.12.20200827.8d3a16e/tests/invalid-elf/invalid-phdr-offset and new/patchelf-0.13.20210805.a949ff2/tests/invalid-elf/invalid-phdr-offset differ Binary files old/patchelf-0.12.20200827.8d3a16e/tests/invalid-elf/invalid-shdr-name and new/patchelf-0.13.20210805.a949ff2/tests/invalid-elf/invalid-shdr-name differ Binary files old/patchelf-0.12.20200827.8d3a16e/tests/invalid-elf/invalid-shrstrtab-idx and new/patchelf-0.13.20210805.a949ff2/tests/invalid-elf/invalid-shrstrtab-idx differ Binary files old/patchelf-0.12.20200827.8d3a16e/tests/invalid-elf/invalid-shrstrtab-nonterm and new/patchelf-0.13.20210805.a949ff2/tests/invalid-elf/invalid-shrstrtab-nonterm differ Binary files old/patchelf-0.12.20200827.8d3a16e/tests/invalid-elf/invalid-shrstrtab-size and new/patchelf-0.13.20210805.a949ff2/tests/invalid-elf/invalid-shrstrtab-size differ Binary files old/patchelf-0.12.20200827.8d3a16e/tests/invalid-elf/invalid-shrstrtab-zero and new/patchelf-0.13.20210805.a949ff2/tests/invalid-elf/invalid-shrstrtab-zero differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/patchelf-0.12.20200827.8d3a16e/tests/invalid-elf.sh new/patchelf-0.13.20210805.a949ff2/tests/invalid-elf.sh --- old/patchelf-0.12.20200827.8d3a16e/tests/invalid-elf.sh 1970-01-01 01:00:00.000000000 +0100 +++ new/patchelf-0.13.20210805.a949ff2/tests/invalid-elf.sh 1970-01-01 01:00:01.000000000 +0100 @@ -0,0 +1,40 @@ +#! /bin/sh -u + +# Usage: killed_by_signal $? +# +# Returns true if the exit code indicates that the program was killed +# by a signal. This works because the exit code of processes that were +# killed by a signal is 128 plus the signal number. +killed_by_signal() { + [ $1 -ge 128 ] +} + + +# The directory containing all our input files. +TEST_DIR=$(dirname $(readlink -f $0))/invalid-elf + +# Each test case is listed here. The names should roughly indicate +# what makes the given ELF file invalid. +TEST_CASES="invalid-shrstrtab-idx invalid-shrstrtab-size invalid-shrstrtab-zero + invalid-shrstrtab-nonterm invalid-shdr-name invalid-phdr-offset" + +FAILED_TESTS="" + +for tcase in $TEST_CASES; do + if [ ! -r "$TEST_DIR/$tcase" ]; then + echo "Cannot read test case: $tcase" + exit 1 + fi + + ../src/patchelf --output /dev/null "$TEST_DIR/$tcase" + if killed_by_signal $?; then + FAILED_TESTS="$FAILED_TESTS $tcase" + fi +done + +if [ -z "$FAILED_TESTS" ]; then + exit 0 +else + echo "Failed tests: $FAILED_TESTS" + exit 1 +fi diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/patchelf-0.12.20200827.8d3a16e/tests/no-rpath-pie-powerpc.sh new/patchelf-0.13.20210805.a949ff2/tests/no-rpath-pie-powerpc.sh --- old/patchelf-0.12.20200827.8d3a16e/tests/no-rpath-pie-powerpc.sh 1970-01-01 01:00:00.000000000 +0100 +++ new/patchelf-0.13.20210805.a949ff2/tests/no-rpath-pie-powerpc.sh 1970-01-01 01:00:01.000000000 +0100 @@ -0,0 +1,54 @@ +#! /bin/sh -e +set -x +SCRATCH=scratch/no-rpath-pie-powerpc + +no_rpath_bin="${srcdir}/no-rpath-prebuild/no-rpath-pie-powerpc" + +if [ ! -f $no_rpath_bin ]; then + echo "no 'no-rpath' binary for '$ARCH' in '${srcdir}/no-rpath-prebuild'" + exit 1 +fi + +rm -rf ${SCRATCH} +mkdir -p ${SCRATCH} + +cp $no_rpath_bin ${SCRATCH}/no-rpath + +oldRPath=$(../src/patchelf --print-rpath ${SCRATCH}/no-rpath) +if test -n "$oldRPath"; then exit 1; fi +../src/patchelf \ + --set-interpreter "$(../src/patchelf --print-interpreter ../src/patchelf)" \ + --set-rpath /foo:/bar:/xxxxxxxxxxxxxxx ${SCRATCH}/no-rpath + +newRPath=$(../src/patchelf --print-rpath ${SCRATCH}/no-rpath) +if ! echo "$newRPath" | grep -q '/foo:/bar'; then + echo "incomplete RPATH" + exit 1 +fi + +# Tests for powerpc PIE endianness regressions +readelfData=$(readelf -l ${SCRATCH}/no-rpath 2>&1) + +if [ $(echo "$readelfData" | grep --count "PHDR") != 1 ]; then + # Triggered if PHDR errors appear on stderr + echo "Unexpected number of occurences of PHDR in readelf results" + exit 1 +fi + +virtAddr=$(echo "$readelfData" | grep "PHDR" | awk '{print $3}') +if [ "$virtAddr" != "0x00000034" ]; then + # Triggered if the virtual address is the incorrect endianness + echo "Unexpected virt addr, expected [0x00000034] got [$virtAddr]" + exit 1 +fi + +echo "$readelfData" | grep "LOAD" | while read -r line ; do + align=$(echo "$line" | awk '{print $NF}') + if [ "$align" != "0x10000" ]; then + # Triggered if the target arch was not detected properly + echo "Unexpected Align for LOAD segment, expected [0x10000] got [$align]" + echo "Load segment: [$line]" + exit 1 + fi +done + Binary files old/patchelf-0.12.20200827.8d3a16e/tests/no-rpath-prebuild/no-rpath-pie-powerpc and new/patchelf-0.13.20210805.a949ff2/tests/no-rpath-prebuild/no-rpath-pie-powerpc differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/patchelf-0.12.20200827.8d3a16e/tests/plain-needed.sh new/patchelf-0.13.20210805.a949ff2/tests/plain-needed.sh --- old/patchelf-0.12.20200827.8d3a16e/tests/plain-needed.sh 1970-01-01 01:00:01.000000000 +0100 +++ new/patchelf-0.13.20210805.a949ff2/tests/plain-needed.sh 1970-01-01 01:00:01.000000000 +0100 @@ -1,4 +1,25 @@ #! /bin/sh set -e + +SCRATCH=scratch/$(basename $0 .sh) +MAIN_ELF="${SCRATCH}/main" + +PATCHELF="../src/patchelf" + +rm -rf ${SCRATCH} +mkdir -p ${SCRATCH} +cp main ${SCRATCH}/ + echo "Confirming main requires libfoo" -../src/patchelf --print-needed main | grep -q libfoo.so +${PATCHELF} --print-needed "${MAIN_ELF}" | grep -q libfoo.so + +echo "Testing --add-needed functionality" +${PATCHELF} --add-needed bar.so "${MAIN_ELF}" +${PATCHELF} --print-needed "${MAIN_ELF}" | grep -q bar.so + +echo "Testing --remove-needed functionality" +${PATCHELF} --remove-needed bar.so "${MAIN_ELF}" +if ${PATCHELF} --print-needed "${MAIN_ELF}" | grep -q bar.so; then + echo "ERROR: --remove-needed did not eliminate bar.so!" + exit 1 +fi diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/patchelf-0.12.20200827.8d3a16e/version new/patchelf-0.13.20210805.a949ff2/version --- old/patchelf-0.12.20200827.8d3a16e/version 2020-08-27 14:12:55.000000000 +0200 +++ new/patchelf-0.13.20210805.a949ff2/version 2021-08-05 14:55:29.000000000 +0200 @@ -1 +1 @@ -0.12.20200827.8d3a16e +0.13.20210805.a949ff2