diff options
-rw-r--r-- | .github/workflows/ci.yml | 2 | ||||
-rw-r--r-- | .github/workflows/publish.yml | 6 | ||||
-rw-r--r-- | src/patchelf.cc | 58 | ||||
-rw-r--r-- | tests/Makefile.am | 6 | ||||
-rwxr-xr-x | tests/rename-dynamic-symbols.sh | 6 | ||||
-rwxr-xr-x | tests/repeated-updates.sh | 39 | ||||
-rwxr-xr-x | tests/set-rpath-library.sh | 10 |
7 files changed, 110 insertions, 17 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a62aced..d52a45c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: cachix/install-nix-action@v19 + - uses: cachix/install-nix-action@v20 - run: nix-build -A hydraJobs.release ubuntu: runs-on: ubuntu-latest diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f330171..c7ed337 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 - - uses: cachix/install-nix-action@v19 + - uses: cachix/install-nix-action@v20 - name: Build tarballs run: | nix build -L .#hydraJobs.tarball @@ -31,7 +31,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 - - uses: cachix/install-nix-action@v19 + - uses: cachix/install-nix-action@v20 - name: Build windows executable run: | nix build -L .#patchelf-win32 .#patchelf-win64 @@ -89,7 +89,7 @@ jobs: rm -f dist/* cd patchelf-* ./configure --prefix /patchelf - make check + make check || (cat tests/test-suite.log; exit 1) make install-strip cd - tar -czf ./dist/patchelf-\$(cat patchelf-*/version)-\$(uname -m).tar.gz -C /patchelf . diff --git a/src/patchelf.cc b/src/patchelf.cc index b78210c..1a90edb 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -800,12 +800,17 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary() page of other segments. */ Elf_Addr startPage = 0; Elf_Addr firstPage = 0; + unsigned alignStartPage = getPageSize(); for (auto & phdr : phdrs) { - Elf_Addr thisPage = roundUp(rdi(phdr.p_vaddr) + rdi(phdr.p_memsz), getPageSize()); + Elf_Addr thisPage = rdi(phdr.p_vaddr) + rdi(phdr.p_memsz); if (thisPage > startPage) startPage = thisPage; if (rdi(phdr.p_type) == PT_PHDR) firstPage = rdi(phdr.p_vaddr) - rdi(phdr.p_offset); + unsigned thisAlign = rdi(phdr.p_align); + alignStartPage = std::max(alignStartPage, thisAlign); } + startPage = roundUp(startPage, alignStartPage); + debug("last page is 0x%llx\n", (unsigned long long) startPage); debug("first page is 0x%llx\n", (unsigned long long) firstPage); @@ -826,11 +831,16 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary() replaceSection(getSectionName(shdrs.at(i)), rdi(shdrs.at(i).sh_size)); i++; } + bool moveHeaderTableToTheEnd = rdi(hdr()->e_shoff) < pht_size; /* Compute the total space needed for the replaced sections */ off_t neededSpace = 0; for (auto & s : replacedSections) neededSpace += roundUp(s.second.size(), sectionAlignment); + + off_t headerTableSpace = roundUp(rdi(hdr()->e_shnum) * rdi(hdr()->e_shentsize), sectionAlignment); + if (moveHeaderTableToTheEnd) + neededSpace += headerTableSpace; debug("needed space is %d\n", neededSpace); Elf_Off startOffset = roundUp(fileContents->size(), getPageSize()); @@ -860,24 +870,48 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary() startPage = startOffset; } - /* Add a segment that maps the replaced sections into memory. */ wri(hdr()->e_phoff, sizeof(Elf_Ehdr)); - phdrs.resize(rdi(hdr()->e_phnum) + 1); - wri(hdr()->e_phnum, rdi(hdr()->e_phnum) + 1); - Elf_Phdr & phdr = phdrs.at(rdi(hdr()->e_phnum) - 1); - wri(phdr.p_type, PT_LOAD); - wri(phdr.p_offset, startOffset); - wri(phdr.p_vaddr, wri(phdr.p_paddr, startPage)); - wri(phdr.p_filesz, wri(phdr.p_memsz, neededSpace)); - wri(phdr.p_flags, PF_R | PF_W); - wri(phdr.p_align, getPageSize()); + bool needNewSegment = true; + auto& lastSeg = phdrs.back(); + /* Try to extend the last segment to include replaced sections */ + if (!phdrs.empty() && + rdi(lastSeg.p_type) == PT_LOAD && + rdi(lastSeg.p_flags) == (PF_R | PF_W) && + rdi(lastSeg.p_align) == alignStartPage) { + auto segEnd = roundUp(rdi(lastSeg.p_offset) + rdi(lastSeg.p_memsz), getPageSize()); + if (segEnd == startOffset) { + auto newSz = startOffset + neededSpace - rdi(lastSeg.p_offset); + wri(lastSeg.p_filesz, wri(lastSeg.p_memsz, newSz)); + needNewSegment = false; + } + } + + if (needNewSegment) { + /* Add a segment that maps the replaced sections into memory. */ + phdrs.resize(rdi(hdr()->e_phnum) + 1); + wri(hdr()->e_phnum, rdi(hdr()->e_phnum) + 1); + Elf_Phdr & phdr = phdrs.at(rdi(hdr()->e_phnum) - 1); + wri(phdr.p_type, PT_LOAD); + wri(phdr.p_offset, startOffset); + wri(phdr.p_vaddr, wri(phdr.p_paddr, startPage)); + wri(phdr.p_filesz, wri(phdr.p_memsz, neededSpace)); + wri(phdr.p_flags, PF_R | PF_W); + wri(phdr.p_align, alignStartPage); + } normalizeNoteSegments(); /* Write out the replaced sections. */ Elf_Off curOff = startOffset; + + if (moveHeaderTableToTheEnd) { + debug("Moving the shtable to offset %d\n", curOff); + wri(hdr()->e_shoff, curOff); + curOff += headerTableSpace; + } + writeReplacedSections(curOff, startPage, startOffset); assert(curOff == startOffset + neededSpace); @@ -2136,7 +2170,7 @@ void ElfFile<ElfFileParamNames>::renameDynamicSymbols(const std::unordered_map<s } } - if (changed) + if (!extraStrings.empty()) { auto newStrTabSize = strTab.size() + extraStrings.size(); auto& newSec = replaceSection(".dynstr", newStrTabSize); diff --git a/tests/Makefile.am b/tests/Makefile.am index b3f0b97..0b648f8 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -43,6 +43,7 @@ src_TESTS = \ replace-needed.sh \ replace-add-needed.sh \ add-debug-tag.sh \ + repeated-updates.sh \ empty-note.sh \ print-execstack.sh \ modify-execstack.sh \ @@ -120,7 +121,7 @@ check_DATA = libbig-dynstr.debug # - with libtool, it is difficult to control options # - with libtool, it is not possible to compile convenience *dynamic* libraries :-( check_PROGRAMS += libfoo.so libfoo-scoped.so libbar.so libbar-scoped.so libsimple.so libsimple-execstack.so libbuildid.so libtoomanystrtab.so \ - phdr-corruption.so many-syms-main libmany-syms.so + phdr-corruption.so many-syms-main libmany-syms.so liboveralign.so libbuildid_so_SOURCES = simple.c libbuildid_so_LDFLAGS = $(LDFLAGS_sharedlib) -Wl,--build-id @@ -144,6 +145,9 @@ libbar_scoped_so_LDFLAGS = $(LDFLAGS_sharedlib) libsimple_so_SOURCES = simple.c libsimple_so_LDFLAGS = $(LDFLAGS_sharedlib) -Wl,-z,noexecstack +liboveralign_so_SOURCES = simple.c +liboveralign_so_LDFLAGS = $(LDFLAGS_sharedlib) -Wl,-z,max-page-size=0x10000 + libsimple_execstack_so_SOURCES = simple.c libsimple_execstack_so_LDFLAGS = $(LDFLAGS_sharedlib) -Wl,-z,execstack diff --git a/tests/rename-dynamic-symbols.sh b/tests/rename-dynamic-symbols.sh index 6fabb33..dc33e1b 100755 --- a/tests/rename-dynamic-symbols.sh +++ b/tests/rename-dynamic-symbols.sh @@ -82,3 +82,9 @@ ${PATCHELF} --rename-dynamic-symbols ../map * echo "# Run the patched tool and libraries" env LD_BIND_NOW=1 LD_LIBRARY_PATH=${PWD} ./many-syms-main + +# Test that other switches still work when --rename-dynamic-symbols has no effect +echo "SYMBOL_THAT_DOESNT_EXIST ANOTHER_NAME" > map +${PATCHELF} --set-rpath changed_rpath --rename-dynamic-symbols map --output libnewrpath.so "$full_lib_name" +[ "$(${PATCHELF} --print-rpath libnewrpath.so)" = changed_rpath ] || exit 1 + diff --git a/tests/repeated-updates.sh b/tests/repeated-updates.sh new file mode 100755 index 0000000..669b11d --- /dev/null +++ b/tests/repeated-updates.sh @@ -0,0 +1,39 @@ +#! /bin/sh -e + +SCRATCH=scratch/$(basename "$0" .sh) +PATCHELF=$(readlink -f "../src/patchelf") + +rm -rf "${SCRATCH}" +mkdir -p "${SCRATCH}" + +cp simple "${SCRATCH}/" +cp libfoo.so "${SCRATCH}/" +cp libbar.so "${SCRATCH}/" + +cd "${SCRATCH}" + +${PATCHELF} --add-needed ./libbar.so simple + +############################################################################### +# Test that repeatedly modifying a string inside a shared library does not +# corrupt it due to the addition of multiple PT_LOAD entries +############################################################################### +load_segments_before=$(readelf -W -l libbar.so | grep -c LOAD) + +for _ in $(seq 1 100) +do + ${PATCHELF} --set-soname ./libbar.so libbar.so + ${PATCHELF} --set-soname libbar.so libbar.so + ./simple || exit 1 +done + +load_segments_after=$(readelf -W -l libbar.so | grep -c LOAD) + +############################################################################### +# To be even more strict, check that we don't add too many extra LOAD entries +############################################################################### +echo "Segments before: ${load_segments_before} and after: ${load_segments_after}" +if [ "${load_segments_after}" -gt $((load_segments_before + 2)) ] +then + exit 1 +fi diff --git a/tests/set-rpath-library.sh b/tests/set-rpath-library.sh index 55661a1..6ae1715 100755 --- a/tests/set-rpath-library.sh +++ b/tests/set-rpath-library.sh @@ -14,6 +14,7 @@ mkdir -p "${SCRATCH}/libsB" cp main-scoped "${SCRATCH}/" cp libfoo-scoped.so "${SCRATCH}/libsA/" cp libbar-scoped.so "${SCRATCH}/libsB/" +cp liboveralign.so "${SCRATCH}/" oldRPath=$(../src/patchelf --print-rpath "${SCRATCH}"/main-scoped) if test -z "$oldRPath"; then oldRPath="/oops"; fi @@ -56,3 +57,12 @@ if test "$exitCode" != 46; then echo "bad exit code!" exit 1 fi + +# ALL loads should have the same alignment +lib="${SCRATCH}/liboveralign.so" +../src/patchelf --set-rpath "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "$lib" +num_alignments=$(${READELF} -W -l "${lib}" | awk '/LOAD/ { print $NF }' | sort -u | wc -l) +echo "$num_alignments" +if test "${num_alignments}" -ne "1"; then + exit 1 +fi |