summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml2
-rw-r--r--.github/workflows/publish.yml6
-rw-r--r--src/patchelf.cc58
-rw-r--r--tests/Makefile.am6
-rwxr-xr-xtests/rename-dynamic-symbols.sh6
-rwxr-xr-xtests/repeated-updates.sh39
-rwxr-xr-xtests/set-rpath-library.sh10
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